【问题标题】:React Re-renders When Parent State is Changed Through Child Component当通过子组件更改父状态时 React 重新渲染
【发布时间】:2019-01-08 08:37:49
【问题描述】:

在 App.js 中,我将 setURL(page){ ... } 作为道具传递给 HealthForm。 在 HealthForm 中,我有一个输入字段,它接受一个 URL 字符串和一个按钮,该按钮启动对我的后端服务器的 fetch 调用,并且在 promise 对象中接收到一些数据。我还在 promiseStatus 函数中调用that.props.changeUrl(that.state.someURL);,因为这是我唯一可以放置它而不会收到以下警告的地方:

Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

但是,每次调用 that.props.changeUrl(that.state.someURL) 时,页面都会重新呈现。基本上——输入字段和由于 fetch 调用而呈现的附加函数——全部重置。不过 App.js 中的 url 状态会更新。

为什么我在调用父 props 时会重新渲染整个页面?

如果 that.props.changeUrl(that.state.someURL) 被简单地删除,应用程序不会重新渲染,但当然它不会改变应用程序状态

我需要页面不要重新渲染,因为重要信息是在 fetch 调用之后渲染的,由于重新渲染会重置该路由,因此无法看到。

App.js

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            url: '',

        };
        this.setURL = this.setURL.bind(this);
    }

    setURL(link) {
        this.setState({
            url: link
        });
    }

    render(){
        return(

            <MuiThemeProvider>
                <Router>
                    <div className="App">
                        <Route path="/" component={Header}></Route>

                        <Route path="/health" component={()=>(
                            <HealthForm changeUrl={this.setURL}/>)}></Route>

                        <Route path="/path1" component={wForm}></Route>
                        <Route path="/path2" component={xForm}></Route>
                        <Route path="/path3" component={yForm}></Route>
                        <Route path="/path4" component={zForm}></Route>

                    </div>
                </Router>
            </MuiThemeProvider>
        );
    }   
}

HealthForm.js

class HealthForm extends React.Component {
  constructor(props) {
   super(props);
    this.state = {
        exampleURL: '',
        exampleURLError: '',
        status: '',
        showStatus: false
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
 }

 validate = () => {
 //…checks for input errors
       }

 handleChange(event) {
    this.setState({
        [event.target.name]: event.target.value
    });
 }

 handleSubmit(event) {
    event.preventDefault();
    const err = this.validate();
    let that = this;
    if (!err) {
                   this.setState({
        exampleURLError: ''
        });
        console.log(this.state);
        var data = this.state.exampleURL

        fetch('htpp://...', {
                    method: 'POST',
                    body: JSON.stringify(data)
                })
                .then((result) => {
                    var promiseStatus = result.text();
                    promiseStatus.then(function (value) {
                        that.setState({
                            status: value,
                            showStatus: true
                        });
                        that.props.changeUrl(that.state.jarvisURL);
                    });
                }).catch((error) => {
                    console.log(error);
                });
    }
 }

 render() {
        return (
            <form>  
            <TextField
              ...
            />
            <br/>

             <Button variant="contained" size="small" color="primary"     onClick={e => this.handleSubmit(e)} >
                Check
            </Button>
            <br />  <br /> 

             ...

            </form>  
        );
 }
}
export default HealthForm;

【问题讨论】:

  • 卸载组件时忘记移除监听器,通常会出现此错误,因此在不再安装的组件的监听器函数中调用this.setState(...)

标签: javascript reactjs react-props


【解决方案1】:

发生这种情况是因为您在 App 组件上调用 setState(),导致它重新渲染,包括重新创建您设置的所有路由。我不确定您使用的是哪个路由器,但它似乎正在重新创建路由下的组件,可能是通过调用再次作为道具传入的component 函数并获取HealthForm 的新实例组件。

我假设应用程序中的所有组件都需要您在App 中存储的状态,这就是您将其放在那里的原因?如果没有,请将其移至 HealthForm 组件中,但如果是这样,也许是时候考虑将状态存储在组件的外部,例如在Redux 之类的状态容器中或Flux 样式的其他东西中。

编辑:我认为你的问题的根源在这里:

<Route path="/health" component={()=>(<HealthForm changeUrl={this.setURL}/>)}></Route>

事实上,一个函数作为component 属性传递,每次都会产生一个新的组件实例。我知道您为什么需要这样做,以便将 setURL() 的引用传递给 HealthForm - 这也是可以通过从组件中提取状态来避免的。

【讨论】:

  • 很好的答案,马特。默认情况下,当状态发生变化时,react 会在组件上运行 render();这是人们似乎经常忽略的组件生命周期的基本规则。另一个问题也会有所帮助:是否在任何时候调用“setState”时都会调用渲染? stackoverflow.com/questions/24718709/…
猜你喜欢
  • 1970-01-01
  • 2021-12-01
  • 2019-12-12
  • 2021-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-07
相关资源
最近更新 更多