React 16不使用Redux等库时,如何结合Firestore渲染异步数据?
嘿,我看你碰到了React异步数据加载后的渲染问题,这其实是新手刚接触React时很容易踩的坑——你直接修改了类实例的categories属性,但React并不会自动监测这种直接赋值的变化,所以数据加载完成后组件也不会重新渲染。咱们来一步步把这个问题搞定:
问题根源分析
你当前的代码里,categories = []是类的一个普通属性,不是React组件的state。当你在componentDidMount里用this.categories.push()修改它时,React完全不知道数据已经更新了,自然不会触发重新渲染,所以页面一直显示"Loading..."。另外,this.categories.push(doc.id).bind(this)这行还有个小错误:push方法返回的是数组的新长度,不是函数,所以bind(this)在这里完全没用,而且箭头函数里的this已经指向组件实例,根本不需要额外绑定。
修改后的代码方案
我们只需要把categories移到组件的state里,并用setState来更新数据,这样React就能监测到变化并重新渲染组件了。
修改后的CategoryButtonSet.js
import React from 'react'; import CategoryButton from './CategoryButton'; import { test } from '../shared/Firebase'; export default class CategoryButtonSet extends React.Component { // 把categories放到state中 state = { categories: [] }; componentDidMount() { test().then((querySnapshot) => { // 用map更简洁地提取所有文档id const categoryList = querySnapshot.docs.map(doc => doc.id); // 用setState更新state,触发组件重新渲染 this.setState({ categories: categoryList }); }); } render() { const { categories } = this.state; if (categories.length !== 0) { console.log(categories); return ( <div className="category-block"> This block is for the category button set. {categories.map(category => ( <CategoryButton key={category} name={category} /> ))} </div> ); } else { return <div>Loading...</div>; } } }
CategoryButton.js 无需修改
你的子组件代码是没问题的,保持原样即可:
import React from 'react'; export default (props) => { return ( <div className="category-block__button"> Here's the category button. <button>{props.name}</button> </div> ); }
为什么这样改能解决问题?
React组件的更新机制依赖于state或props的变化:当你调用setState时,React会知道组件的状态发生了改变,然后触发render方法重新渲染组件,这时候就能用上刚从Firestore加载好的categories数据了。
这样修改后,当Firestore的数据加载完成,组件会自动更新并显示所有的分类按钮,不会再停留在加载状态啦!
内容的提问来源于stack exchange,提问作者Mingyu Jeon

