React父子组件传参问题:点击前Props未定义报错求助
不用抱歉呀,新手遇到这种状态初始化的问题太正常了😉,我来帮你梳理下问题根源和解决办法:
问题分析
你遇到的报错核心原因是:初始渲染阶段父组件的categoryData状态还未被设置,此时传给子组件CategoryData的categoryData props是undefined。而子组件里直接对this.state.categoryData(初始是[],之后又在componentDidMount设为[],但父组件传的props没同步过来)调用Object.keys(),当props是undefined时,就会触发“无法读取undefined的属性”这类报错。
另外还有个小问题:子组件把props存到自己的state里,但没有监听props变化来更新state,就算父组件点击后传了新的props,子组件的state也不会自动更新,这也是个潜在的bug哦。
解决办法
这里给你几个简单有效的方案,按推荐程度排序:
方案1:父组件初始化state时给categoryData默认值
在父组件的构造函数里,给categoryData初始化为空对象,这样初始渲染时传给子组件的就是一个空对象,而不是undefined:
class Categories extends Component { constructor(props) { super(props); this.state = { data: [], categoryData: {} // 新增默认空对象 }; } // ... 其他代码不变 }
方案2:子组件直接使用props(推荐,更符合React数据流)
子组件不需要把props存到自己的state里,直接用this.props.categoryData就好,同时加上空值判断避免报错:
class CategoryData extends Component { // 可以去掉constructor和componentDidMount,不需要自己的state了 render() { // 先拿到props里的数据,为空的话用空对象兜底 const categoryData = this.props.categoryData || {}; return ( <div className='basicData'> <h1>General information</h1> <ul> {Object.keys(categoryData).map((item) => { return ( <li key={item}> <span> {item}</span> : <span>{categoryData[item]}</span> </li> ) })} </ul> </div> ); } }
这样既解决了初始undefined的问题,又能自动同步父组件传来的最新props,不用手动维护子组件的state。
方案3:子组件里添加空值判断兜底(保留子组件state的情况)
如果一定要用子组件的state,那需要在componentDidUpdate里监听props变化更新state,同时渲染时加兜底:
class CategoryData extends Component { constructor(props) { super(props); // 用props的初始值初始化state,没有的话用空对象 this.state = { categoryData: props.categoryData || {} }; } // 监听props变化,更新state componentDidUpdate(prevProps) { if (prevProps.categoryData !== this.props.categoryData) { this.setState({ categoryData: this.props.categoryData || {} }); } } render() { const { categoryData } = this.state; return ( <div className='basicData'> <h1>General information</h1> <ul> {/* 先判断categoryData是否存在,再执行map */} {categoryData && Object.keys(categoryData).map((item) => { return ( <li key={item}> <span> {item}</span> : <span>{categoryData[item]}</span> </li> ) })} </ul> </div> ); } }
额外小提醒
父组件里的changeCategory方法,用event.currentTarget.textContent来取索引可能不稳定(比如文本里有多个空格或者其他字符),建议直接把item作为参数传进去,比如:
<li className='category' key={item} onClick={() => this.changeCategory(item)}> <span className='category-item'> {item}</span> </li> // 然后修改changeCategory方法: changeCategory(item) { this.setState({ categoryData: this.state.data[item] }); }
这样更可靠哦~
内容的提问来源于stack exchange,提问作者pawell67

