【问题标题】:React component re-render on prop changeReact 组件在 prop 更改时重新渲染
【发布时间】:2023-03-28 17:50:02
【问题描述】:

import React from 'react';
import { render } from 'react-dom';

const x = [];

const App = props => <div style={styles}>{JSON.stringify(x)}</div>;

render(<App queries={x} />, document.getElementById('root'));

setInterval(x => x.push(Math.random()), 1000, x);
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>React App</title>
</head>

<body>
  <div id="root"></div>
</body>

</html>

当我改变作为道具传递给它的对象时,为什么 App 组件不重新呈现。

每当我关闭并重新打开 React 开发工具时,它都会显示新的道具。

这里发生了什么?请帮我理解。

谢谢!

【问题讨论】:

标签: javascript reactjs


【解决方案1】:

这种情况下的答案与setState无关。当状态改变时,Sure React 会自动重新渲染(只要你没有做类似使用 PureComponent 然后改变数组或对象的事情)。但是您正在从根进行渲染,这与反应状态无关,因为您的状态存在于反应之外。

来自react docs on render

如果 React 元素之前被渲染到容器中,这将 对其执行更新并仅根据需要更改 DOM 反映最新的 React 元素。

因此需要在根级别再次调用 render 以使 React 协调更改。制作一个新的对象/数组也是更好的做法。所以在这种情况下你可以做x = x.concat(..) 然后再次渲染。

let x = [];

const App = props => <div>{props.queries.toString()}</div>;

const renderApp = (queries) => ReactDOM.render(<App queries={queries} />, document.getElementById('root'));

setInterval(() => {
  x = x.concat(Math.random());
  renderApp(x);
}, 1000);

小提琴:https://jsfiddle.net/ferahl/p7zhs55q/1/

当然,更常见的做法是在 React 组件中使用本地 React 状态,但最好了解这一点,如果您不使用 Redux 之类的东西,这就是您如何将一些全局状态实现为 React 的方式。

【讨论】:

  • 这可能有效,但这不是 react 的预期用途。当您认为基础数据已更改时,您将不得不手动触发重新渲染,这与调用forceUpdate() 大致相同。相反,OP 应该创建一个负责更新的容器组件。如果您进行手动渲染,您将失去批量更新等的所有优势。
  • 我同意这是不寻常的,也许他们的 OP 不知道反应状态,我只是在提出问题时回答问题(将外部状态传递给反应)。但是调用 setState 也会重新渲染组件,除非你在 shouldComponentUpdate 中实现了检查。在上述情况下,您可以使用 if (state !== nextState) renderApp(nextState) 做类似的事情
  • 没有组件只会在以下情况下重新渲染:1) 父渲染函数被调用 2) this.setState 在组件中被调用 3) this.forceUpdate 被调用(不推荐) - 如果它不是您编写的函数组件,而是一个类,那么也只有当 shouldComponentUpdate 返回 true 时(默认情况下,它对于 React.Component 为 true,并对 React.PureComponent 的 props 和 state 进行浅层相等检查)
  • 但是,如果 props 确实发生了变化,这意味着父级已经更改了它们并且组件被重新渲染(props 来自父级并向下传递给组件,组件本身不会更改 props,唯一状态)
  • 很高兴为您提供帮助,如果您有任何问题,请告诉我。一开始可能会令人困惑,但过一段时间就会变得有意义。正如另一个答案暗示的那样,您通常会更改组件内的状态(不再调用渲染)。如果App 具有状态并将其传递给子组件,则将props 传递给子组件,依此类推。
【解决方案2】:

改变作为 props 传递的数据不会触发组件的重新渲染。没有观察者注意到您对该对象的可能触发它的突变。如果您有数据在组件的生命周期内发生更改you need to use state

import React, {Component} from 'react';
import { render } from 'react-dom';

const x = [];

class App extends Component {
    state = {
        queries: this.props.queries,
    };

    componentDidMount() {
        setInterval(
            () => this.setState(state => ({queries: [...state.queries, Math.random()]}))
        ), 1000);
    }

    render() {
        return <div style={styles}>{JSON.stringify(this.state.queries)}</div>;
    }
}

render(<App queries={x} />, document.getElementById('root'));

此代码将使用通过 props 传递的查询初始化组件状态。当组件确实挂载时,它将开始间隔,该间隔将向状态附加一个新的随机数。调用setState() 将触发新状态的重新渲染。永远不要改变道具。

【讨论】: