为什么这个React组件会渲染两次?

9
问题在于组件被渲染了两次,一次是使用初始状态,另一次是在 axios promise 的 setState 方法之后再次渲染。为什么会出现这种情况,我该如何解决这个问题。
我已经尝试使用 componentWillMount 和 componentDidMount 两种方法,但由于自己的菜鸟水平,未能成功找到原因。
export default class Dashboard extends Component{

  constructor(props) {
    super(props)
    this.state = {
      data: {
        okay: 'lul'
      }
    }
  }

  componentWillMount() {
    axios
      .get('/api/data?param1='+this.props.location.state.param1+'&param2='+this.props.location.state.param2)
      .then(res => {
        if (res.status != 401) {
          if(res.err)
            console.log('Error while retrieving: ', res.err)
          else {
            this.setState({
              data: res.data.data
            })
          }
        } else {
          console.log('Unauthorized!');
        }
      })
  }

  render() {
    return (
        <Segment inverted vertical>
          <CardContainer data={this.state.data}/>
        </Segment>
    )
  }
}

非常赞赏与React/JS/通用编程有关的基本建议。


你认为这是个问题吗?如果你不想在数据被获取之前显示任何内容,你可以在渲染方法中检查this.state.data并且如果还没有数据要显示就返回null。每次调用setState都会导致render方法被调用。 - Håken Lid
@HåkenLid 你提供的解决方案完美地运行了。但是如何只使用正确的数据一次渲染组件呢?我需要在构造函数中编写axios请求并设置初始状态吗? - psvs
这种行为符合 React 的预期,而非 bug。调用 render 方法的成本相当低,因为 React 会以高效的方式更新 DOM。在某些情况下,您可能希望调整重新渲染发生的时间。React 文档中讨论了这个问题: [shouldComponentUpdate In Action] (https://reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate-in-action)。 - Håken Lid
1个回答

10

在componentWillMount中有一个异步请求,因此在请求完成之前,组件就被渲染了。但是一旦异步请求成功,你会调用setState函数,这会触发重新渲染,从而导致组件被渲染两次。

这是一种预期的行为。

你可以参考这个问题了解更多细节:

在React中使用componentWillMount或componentDidMount生命周期函数处理异步请求

根据文档:

componentWillMount()在挂载发生之前被调用。在render()方法调用之前调用,因此在该方法中同步调用setState()不会触发额外的渲染。

这意味着,如果你写入以下代码:

componentWillMount() {
   this.setState({count: 1});
}

状态将在初始渲染中反映,并且不会触发无需渲染。但是,如果您有异步方法,则在调用setState内部时,如果异步请求在已经调用渲染后完成,则可能会触发额外的渲染。

为了更加强调这一事实,您不应再使用componentWillMount,因为React计划从未来的重大发布中删除此方法。而是使用componentDidMount。


我明白了。因此,每当状态被设置时,渲染方法都会被调用。但是他们说React很“智能”,不会进行冗余的渲染。你怎么看? - psvs
React不会在DOM中执行冗余渲染,但它仍然调用render方法,在这里它执行虚拟DOM差异比较并决定是否进行渲染。 - Shubham Khatri
它写道:“在此方法中调用setState()将触发额外的渲染,但保证在同一时刻进行刷新。这保证了即使在这种情况下render()会被调用两次,用户也不会看到中间状态。”我只有两个状态实例,一个是initial,另一个是setState。那么中间状态是指initial吗? - psvs
在componentWillMount同步调用setState不会触发额外的渲染,但由于这里有一个额外的异步方法,它会触发额外的重新渲染。 - Shubham Khatri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接