ReactJS 生命周期 (v. 16.4) 中传递数据的正确模式是什么
What is the correct pattern in ReactJS lifecycle (v. 16.4) to pass data
ReactJS 生命周期 (v. 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;
然后我的子组件应该从 props 中获取 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 中)然后返回,table 细胞不见了。这可能是因为我错误地实施了生命周期,或者是因为我的密钥有问题...我不知道。
更新:
这是我的路由代码:
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();
}
记录的输出是 {}
。即使父级的状态已更改,它也永远不会更新。
React 知道你的 child 组件的 props 已经改变,并且会 re-render 为你的组件。您不需要在 child 中使用任何生命周期方法或状态。只需在 child 的渲染方法 中从道具 重新生成 rows
和 columns
并立即使用它们。
您的 parent 组件没问题,但您必须记住 setState
是异步的。第一次调用 render
将传递空 object。这是渲染获取的数据的常见问题。 setState
更新状态并强制重新渲染 - 第二个 render
使用更新的数据。
Child 可以知道是否更换道具。如果没有,则阻止渲染 child 直到数据准备好。在这种情况下,componentDidUpdate
使 child 知道 prop 的变化。没关系,也可以在更新时调用 componentDidMount
中的 this.createTableData();
(未在初始渲染时调用)。
注意:componentDidMount
只会调用一次。
如果您仍然有问题,那么首先 console.log
在 parent 渲染中检查它是否被调用了两次以及传递了什么。 child 渲染相同。
您可以使用 shouldComponentUpdate
而不是 componentDidUpdate
(并且 createTableData
只是从渲染中调用)。需要准备数据作为优化方法 - 当并非所有数据都更改时。
您可以在任何地方插入日志记录来跟踪生命周期的内容、时间和参数(数据)。
为了让子props正确初始化(这样你的子组件就可以在componentDidMount()
中正确使用它们),只有当你的parent state被设置时你才应该挂载子组件。
以下是使用异步初始状态渲染组件的方法:
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>
);
}
}
ReactJS 生命周期 (v. 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;
然后我的子组件应该从 props 中获取 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 中)然后返回,table 细胞不见了。这可能是因为我错误地实施了生命周期,或者是因为我的密钥有问题...我不知道。
更新:
这是我的路由代码:
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();
}
记录的输出是 {}
。即使父级的状态已更改,它也永远不会更新。
React 知道你的 child 组件的 props 已经改变,并且会 re-render 为你的组件。您不需要在 child 中使用任何生命周期方法或状态。只需在 child 的渲染方法 中从道具 重新生成 rows
和 columns
并立即使用它们。
您的 parent 组件没问题,但您必须记住 setState
是异步的。第一次调用 render
将传递空 object。这是渲染获取的数据的常见问题。 setState
更新状态并强制重新渲染 - 第二个 render
使用更新的数据。
Child 可以知道是否更换道具。如果没有,则阻止渲染 child 直到数据准备好。在这种情况下,componentDidUpdate
使 child 知道 prop 的变化。没关系,也可以在更新时调用 componentDidMount
中的 this.createTableData();
(未在初始渲染时调用)。
注意:componentDidMount
只会调用一次。
如果您仍然有问题,那么首先 console.log
在 parent 渲染中检查它是否被调用了两次以及传递了什么。 child 渲染相同。
您可以使用 shouldComponentUpdate
而不是 componentDidUpdate
(并且 createTableData
只是从渲染中调用)。需要准备数据作为优化方法 - 当并非所有数据都更改时。
您可以在任何地方插入日志记录来跟踪生命周期的内容、时间和参数(数据)。
为了让子props正确初始化(这样你的子组件就可以在componentDidMount()
中正确使用它们),只有当你的parent state被设置时你才应该挂载子组件。
以下是使用异步初始状态渲染组件的方法:
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>
);
}
}