我有完整的运行代码,但是有缺陷。它是从render()内部调用setState()的。 因此,反应引发反模式警告。
POST https://dialogflow.googleapis.com/v2/{parent=projects/*}/agent:restore
我的逻辑是这样的。在 index.js 父组件中,我具有以下代码。 Constructor()使用初始值调用graphs()来显示图形。用户还具有一个表单,用于指定新值并提交表单。它将使用新值再次运行graphs()并重新渲染该图。
Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount
FormComponent是具有输入字段和如下所示的提交按钮的普通表单。它将回调函数发送到Parent组件,该组件会触发graphs()以及componentWillReceiveProps。
import React, { Component } from 'react';
import FormComponent from './FormComponent';
import PieGraph from './PieGraph';
const initialval = '8998998998';
class Dist extends Component {
constructor() {
this.state = {
checkData: true,
theData: ''
};
this.graphs(initialval);
}
componentWillReceiveProps(nextProps) {
if (this.props.cost !== nextProps.cost) {
this.setState({
checkData: true
});
}
}
graphs(val) {
//Calls a redux action creator and goes through the redux process
this.props.init(val);
}
render() {
if (this.props.cost.length && this.state.checkData) {
const tmp = this.props.cost;
//some calculations
....
....
this.setState({
theData: tmp,
checkData: false
});
}
return (
<div>
<FormComponent onGpChange={recData => this.graphs(recData)} />
<PieGraph theData={this.state.theData} />
</div>
);
}
}
代码运行正常。有更好的方法吗?不用在render()中做setState吗?
答案 0 :(得分:2)
永远不要在渲染中使用setState。之所以不这样做,是因为对于每个setState,您的组件都会重新呈现,因此在render中执行setState会导致无限循环,因此不建议这样做。
不需要checkData布尔变量。您可以在componentWillReceiveProps中直接比较以前的成本和当前成本,如果它们不相等,则使用setState将成本分配给Data。请参阅下面的更新解决方案。
还要开始在所有有状态的组件中使用shouldComponentUpdate方法,以避免不必要的重新渲染。这是每个有状态组件的最佳实践和推荐方法。
import React, { Component } from 'react';
import FormComponent from './FormComponent';
import PieGraph from './PieGraph';
const initialval = '8998998998';
class Dist extends Component {
constructor() {
this.state = {
theData: ''
};
this.graphs(initialval);
}
componentWillReceiveProps(nextProps) {
if (this.props.cost != nextProps.cost) {
this.setState({
theData: this.props.cost
});
}
}
shouldComponentUpdate(nextProps, nextState){
if(nextProps.cost !== this.props.cost){
return true;
}
return false;
}
graphs(val) {
//Calls a redux action creator and goes through the redux process
this.props.init(val);
}
render() {
return (
<div>
<FormComponent onGpChange={recData => this.graphs(recData)} />
{this.state.theData !== "" && <PieGraph theData={this.state.theData} />}
</div>
);
}
}
PS:-上述解决方案适用于React v15版本。
答案 1 :(得分:2)
您不应使用 componentWillReceiveProps ,因为在最新版本中,它是UNSAFE,并且不能与React的异步渲染一起很好地工作。
还有其他方法!
static getDerivedStateFromProps(props, state)
getDerivedStateFromProps在调用渲染之前被调用 初始安装和后续更新上的方法。这应该 返回一个对象以更新状态,或者返回null则不更新任何内容。
所以就您而言
...component code
static getDerivedStateFromProps(props,state) {
if (this.props.cost == nextProps.cost) {
// null means no update to state
return null;
}
// return object to update the state
return { theData: this.props.cost };
}
... rest of code
您也可以使用memoization,但要根据您的情况决定。 链接中有一个示例,您可以通过备忘录和getDerivedStateFromProps获得相同的结果
例如更改道具后更新列表(搜索) 您可以从这里开始:
static getDerivedStateFromProps(props, state) {
// Re-run the filter whenever the list array or filter text change.
// Note we need to store prevPropsList and prevFilterText to detect changes.
if (
props.list !== state.prevPropsList ||
state.prevFilterText !== state.filterText
) {
return {
prevPropsList: props.list,
prevFilterText: state.filterText,
filteredList: props.list.filter(item => item.text.includes(state.filterText))
};
}
return null;
}
对此:
import memoize from "memoize-one";
class Example extends Component {
// State only needs to hold the current filter text value:
state = { filterText: "" };
// Re-run the filter whenever the list array or filter text changes:
filter = memoize(
(list, filterText) => list.filter(item => item.text.includes(filterText))
);
handleChange = event => {
this.setState({ filterText: event.target.value });
};
render() {
// Calculate the latest filtered list. If these arguments haven't changed
// since the last render, `memoize-one` will reuse the last return value.
const filteredList = this.filter(this.props.list, this.state.filterText);
return (
<Fragment>
<input onChange={this.handleChange} value={this.state.filterText} />
<ul>{filteredList.map(item => <li key={item.id}>{item.text}</li>)}</ul>
</Fragment>
);
}
}