ReactJS生命周期(16.4版)中显示父组件中componentDidMount子组件中数据的正确模式是什么?
我有一个应该足够简单的场景,但是它并没有达到我期望的方式。我想将数据从父组件传递到子组件,子组件又将数据转换为可以显示的内容。
我已经阅读了这篇文章https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html,并且正在尝试使模式正确。
以前,我使用componentWillMount
来做到这一点,并且效果很好,但是正如我所说的,它在componentDidMount
上的表现很奇怪。
我的父组件在其componentDidMount
中获取数据,该数据会更新状态,然后将其传递给子组件:
class NewTable extends Component {
constructor(props) {
super(props);
this.state = {
dataSet: {}
}
}
componentDidMount() {
const dataSet = DataSet.getColorDataSet();
this.setState({
dataSet: dataSet
});
}
render() {
return (
<div className="example">
<h2>HTML Table</h2>
<NewKSParser.HTMLTable dataSet={this.state.dataSet} id="myid" />
</div>
);
}
}
export default NewTable;
然后我的子组件应该从道具中拾取dataSet
并显示它:
export class HTMLTable extends Component {
constructor(props) {
super(props);
this.state = {
columns: [],
rows: []
}
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.dataSet !== this.props.dataSet) {
this.createTableData();
}
}
createTableData = () => {
const dataSet = { ...this.props.dataSet };
const rows = dataSet.dataSets ? dataSet.dataSets[0].rows : [];
const metaCols = dataSet.dataSets ? dataSet.dataSets[0].metadata.columns : [];
const columns = this.generateColumns(metaCols);
this.setState({
columns: columns,
rows: rows
});
}
generateColumns = (metaCols) => {
const columns = metaCols.map((col) => {
let obj = {};
obj.id = col.id;
obj.index = col.index;
obj.title = col.title;
return obj;
});
return columns;
};
render() {
const { rows, columns } = this.state;
const tableHeader = columns.map((column) => {
return <th key={column.id}>{column.title}</th>
});
const tableCells = rows.map((row, idx1) => {
return (<tr key={'row_' + idx1}>
{row.cells.map((cellContent, idx2) => <td key={`cell_${idx1}_${idx2}`}>{cellContent}</td>)}
</tr>);
});
return (
<table id={this.props.myid} key={this.props.myid}>
<thead>
<tr>
{tableHeader}
</tr>
</thead>
<tbody>
{tableCells}
</tbody>
</table>
);
}
}
如您所见,我将代码放在componentDidUpdate
中,因为将其放在孩子的componentDidMount
方法中时根本无法正常工作。
我把它放在componentDidUpdate
中似乎很奇怪,我不知道那是否正确。
我遇到的第二个问题是,当我第一次访问该页面时,它可以很好地呈现,但是如果我转到另一个页面(在react-router内)然后又回来,则表单元格中的数据就消失了。可能是因为我实施的生命周期错误,或者是因为我的钥匙有问题...我不知道。
更新:
这是我的路由代码:
class KnowledgeSets extends Component {
render() {
return (
<Router>
<main>
<TopMenu />
<Switch>
<Route exact path="/" render={()=><Home {...this.props} />} />
<Route path="/example" render={()=><Example {...this.props} />} />
<Route path="/htmltablecomponent" render={()=><Table {...this.props} />} />
<Route path="/complexexpandable" render={()=><ComplexExpandable {...this.props} />} />
<Route path="/newdataviewer" render={()=><NewDataViewer {...this.props} />} />
<Route path="/newhtmltablecomponent" render={()=><NewTable {...this.props} />} />
<Route path="/search" render={()=><Search {...this.props} />} />
</Switch>
</main>
</Router>
);
}
}
export default KnowledgeSets;
如果我将子组件更改为...
componentDidMount() {
console.log(this.props.dataSet);
this.createTableData();
}
记录的输出为{}
。即使父级的状态已更改,也永远不会更新。
答案 0 :(得分:1)
React知道您的子组件的道具已更改,并将为您重新渲染该组件。您无需在子级中使用任何生命周期方法或状态。只需从孩子的渲染方法中的道具内部重新生成rows
和columns
,并立即使用它们。
答案 1 :(得分:1)
您的父组件正常,但您必须记住setState
是异步的。第一次调用render
将传递空对象。这是呈现获取的数据时常见的问题。 setState
更新状态并强制重新渲染-第二render
使用更新的数据。
孩子可以知道是否改变道具。如果没有,则阻止渲染子级,直到数据准备就绪为止。在这种情况下,componentDidUpdate
使孩子知道道具的改变。没关系,可以在更新时在this.createTableData();
中调用componentDidMount
(在初始渲染时不调用)。
注意:componentDidMount
仅被调用一次。
如果仍然有问题,则首先在父渲染中console.log
检查它是否被调用了两次以及传递了什么。子渲染也一样。
您可以使用shouldComponentUpdate
代替componentDidUpdate
(以及createTableData
只是从渲染调用)。作为优化方法,需要准备数据-当并非所有数据都被更改时。
您可以在任何地方插入日志记录,以跟踪生命周期的内容,时间和参数(数据)。
答案 2 :(得分:1)
只有在设置了父状态时,才应该挂载子组件,以便正确初始化子道具(然后,子组件可以在componentDidMount()
中正确使用它们)。
这是如何以异步初始状态呈现组件的方法:
class NewTable extends Component {
constructor(props) {
super(props);
this.state = {
dataSet: {},
loading: true
};
}
componentDidMount() {
const dataSet = DataSet.getColorDataSet();
this.setState({
dataSet,
loading: false
});
}
render() {
if (this.state.loading) {
return (<div> Loading </div>)
}
return (
<div className="example">
<h2>
HTML Table
</h2>
<NewKSParser.HTMLTable dataSet={this.state.dataSet} id="myid" />
</div>
);
}
}