React:componentDidMount + setState不重新渲染组件

时间:2016-11-18 18:00:21

标签: javascript reactjs

我很擅长使用componentDidMountsetState来更新自定义组件,这似乎是推荐的做法。下面是一个示例(包括用于获取数据的axios API调用):

import React from 'react';
import {MyComponent} from 'my_component';
import axios from 'axios';


export default class Example extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            data: []
        };
    }

    GetData() {
        return axios.get('http://localhost:5000/<route>');
    }

    componentDidMount() {
        this.GetData().then(
                (resp) => {
                    this.setState(
                        {data: resp.data}
                    )
                }
            )
    }

    render() {
        return (
            <MyComponent data={this.state.data} />
        );
  } 
}

console.log(this.state.data)下方render()显示this.state.data确实已更新(从[]到API返回的任何内容)。但是,问题似乎是MyComponent不会重新呈现componentDidMount。来自Facebook的反应文档:

  

在此方法中设置状态将触发重新渲染。

这似乎不是这样的:MyComponent的构造函数只被调用一次(其中this.props.data = [])并且组件不会再次渲染。如果有人可以解释为什么会这样,以及是否有解决方案或完全不同的方式来完成更新,我会很高兴。

更新

我添加了MyComponent的代码(减去一些不相关的功能,如...所示)。 console.log(data_array)打印一个空数组。

import React from 'react';

class DataWrapper {
  constructor(data) {
    this._data = data;
  }

  getSize() {
    return this._data.length;
  }

     ...
}


export class MyComponent extends React.Component {
      constructor(props) {
        super(props);

        this._dataWrapper = new DataWrapper(this.props.data);

        this.state = {
          data_array: this._dataWrapper,
        };

      }

    render() {
        var {data_array} = this.state;
        console.log(data_array);
        return (
                ...
        );
    }
 }

2 个答案:

答案 0 :(得分:4)

您正在成为this antipattern.

的受害者

MyComponent构造函数中,它只在第一次调用时被调用,通过new DataWrapper传递空数组,现在你有一些本地状态,无论你的父母做什么都不会更新。

拥有一个真理来源,只需一个状态对象(尤其是像ajax响应之类的东西),并通过道具传递它们总是更好。事实上,你可以将MyComponent写成一个简单的函数,而不是类。

class Example extends Component {
  state = { data: [] }

  GetData() { .. }

  componentDidMount() {
    this.GetData().then(res =>
      this.setState({data: new DataWrapper(res.data)})
    )
  }

  render() { return <MyComponent data={this.state.data} /> }
}

...

function MyComponent (props) {
  // props.data will update when your parent calls setState
  // you can also call DataWrapper here if you need MyComponent specific wrapper
  return (
    <div>..</div>
  )
}

答案 1 :(得分:0)

换句话说,Azium的意思是您需要将接收组件变成受控组件。意思是,它根本不应该有状态。直接使用道具。

是的,甚至将其变成功能组件。这可以帮助您牢记功能组件通常没有状态(可以将状态放入其中,但是...分离关注点)。

如果需要从该受控组件中编辑状态,请通过props提供功能并在“主”组件中定义功能。因此,主组件只是将控制权交给孩子。他们想要与父母交谈的任何内容。

我不在这里发布代码,因为您需要做出的修改可以忽略不计。在受控组件中具有this.state的位置,请更改为this.props。