Reputation: 112
I have a custom component called 'Article', and I'm trying to show all articles, by fetching them from a database and rendering them as an 'Article' component.
function renderArticle(doc){
var article = '<Article name="' + doc.data().name + '" content="' + doc.data().content + '" id="' + doc.data().id + '"/>'
document.getElementById('articles-list').appendChild(article)
}
const db = firebase.firestore()
db.collection('articles').get().then((snapshot) => {
snapshot.docs.forEach(doc => {
renderArticle(doc)
})
})
When I run it, the console returns TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
I'm stuck...
Upvotes: 0
Views: 2913
Reputation: 576
AS @Calvin Nunes said you need to pass a Node to document.createElement(), but as you tagged reactjs maybe you could use some advice about it too.
In React the way we have to render data that comes asynchronously (e.g. the docs that you are getting from your DB) is to track them into the component state.
In a first moment we render null or some loading indicator and fire the request to get our data. When the data is loaded we set our state and due to that our component will rerender an we can render what is in the state.
If you are using a component with lifecycles your code would be like:
import React, {Component} from 'react';
class ArticleList extends Component {
constructor(props) {
super(props);
this.state = {
docs: null,
};
}
componentDidMount() {
db.collection('articles').get().then((snapshot) => {
this.setState({docs: snapshot.docs});
})
}
render() {
if (docs === null) return null;
return (
<div>
{docs.map((doc) => {
return (
<Article
content={doc.data().content}
key={doc.data().id}
id={doc.data().id}
name={doc.data().name}
/>
);
})}
</div>
);
}
}
export default ArticleList;
If you are using a function with hooks your code would be like:
import React, {useEffect, useState} from 'react';
import Article from './Article'; // your other component
function ArticleList () {
const [docs, setDocs] = useState(null);
const db = firebase.firestore();
useEffect(() => {
db.collection('articles').get().then((snapshot) => {
setDocs(snapshot.docs);
})
}, [db, setDocs]);
if (docs === null) return null;
return (
<div>
{docs.map((doc) => {
return (
<Article
content={doc.data().content}
key={doc.data().id}
id={doc.data().id}
name={doc.data().name}
/>
);
})}
</div>
);
}
export default ArticleList;
You will probably need to memoize the db object too in order to run the useEffect hooks just once, but this is not the point here.
Upvotes: 1
Reputation: 20924
Use Element.insertAdjecentHTML
to insert a string as HTML. The first parameter is the position where to add the HTML. The second parameter is the HTML to insert.
function renderArticle(doc){
var article = '<Article name="' + doc.data().name + '" content="' + doc.data().content + '" id="' + doc.data().id + '"/>';
document.getElementById('articles-list').insertAdjacentHTML('beforeend', article);
}
But since you are using React you should do it differently. From what I've found you should use the ReactDOM.render
function to render a component on the fly and insert it into the DOM.
function renderArticle(doc){
var article = '<Article name="' + doc.data().name + '" content="' + doc.data().content + '" id="' + doc.data().id + '"/>';
ReactDOM.render(article, document.getElementById('articles-list'));
}
Make sure to import the ReactDOM
module to use it properly. Place the import at the top of your script.
import ReactDOM from 'react-dom'
Upvotes: 0