你能在不调用 setState 的情况下强制 React 组件重新渲染吗?

IT技术 reactjs react-jsx
2021-04-14 23:44:32

我有一个外部的(组件的)可观察对象,我想监听它的变化。当对象更新时,它会发出更改事件,然后我想在检测到任何更改时重新渲染组件。

对于顶级,React.render这是可能的,但在组件中它不起作用(这有一定的意义,因为该render方法只返回一个对象)。

这是一个代码示例:

export default class MyComponent extends React.Component {

  handleButtonClick() {
    this.render();
  }

  render() {
    return (
      <div>
        {Math.random()}
        <button onClick={this.handleButtonClick.bind(this)}>
          Click me
        </button>
      </div>
    )
  }
}

在内部单击按钮会调用this.render(),但这并不是真正导致渲染发生的原因(您可以看到这一点,因为 由 创建的文本{Math.random()}不会改变)。但是,如果我只是调用this.setState()而不是this.render(),它工作正常。

所以我想我的问题是: React 组件是否需要有状态才能重新渲染?有没有办法强制组件按需更新而不改变状态?

6个回答

在类组件中,您可以调用this.forceUpdate()强制重新渲染。

文档:https : //facebook.github.io/react/docs/component-api.html

在函数组件中,没有等效的forceUpdate,但您可以设计一种使用useStatehook强制更新的方法

另一种方式是 this.setState(this.state);
2021-05-21 23:44:32
虽然this.forceUpdate()不是提问者问题的很好解决方案,但它是标题中提出的问题的正确答案。虽然通常应该避免它,但在某些情况下 forceUpdate 是一个很好的解决方案。
2021-05-26 23:44:32
不鼓励使用 forceupdate,请参阅最后一个答案。
2021-05-27 23:44:32
@kar 实际上,可以实现额外的逻辑来shouldComponentUpdate()使您的解决方案无效。
2021-05-28 23:44:32
超出最大调用堆栈大小
2021-06-14 23:44:32

forceUpdate应该避免,因为它偏离了 React 的思维方式。React 文档引用了一个何时forceUpdate可能使用的示例

默认情况下,当您的组件的 state 或 props 发生变化时,您的组件将重新渲染。但是,如果这些隐式更改(例如:对象内部的数据更改而不更改对象本身)或者如果您的 render() 方法依赖于其他一些数据,您可以通过调用告诉 React 它需要重新运行 render()强制性升级()。

但是,我想提出这样的想法,即即使使用深度嵌套的对象,forceUpdate也是不必要的。通过使用不可变数据源跟踪更改变得便宜;更改总是会产生一个新对象,因此我们只需要检查对对象的引用是否已更改。您可以使用库Immutable JS将不可变数据对象实现到您的应用程序中。

通常你应该尽量避免使用 forceUpdate() 并且只在 render() 中从 this.props 和 this.state 中读取。这使您的组件“纯”,并且您的应用程序更简单、更高效。强制性升级()

更改要重新渲染的元素的键将起作用。通过 state 设置元素上的 key 属性,然后当您想要更新设置状态以拥有新键时。

<Element key={this.state.key} /> 

然后发生变化,您重置密钥

this.setState({ key: Math.random() });

我想指出,这将替换键正在更改的元素。这可能很有用的一个例子是当您有一个文件输入字段时,您希望在上传图像后重置该字段。

虽然 OP 问题的真正答案是forceUpdate()我发现此解决方案在不同情况下很有帮助。我还想指出,如果您发现自己在使用,forceUpdate您可能需要查看您的代码,看看是否有其他方法可以做事。

注 1-9-2019:

以上(更改键)将完全替换元素。如果您发现自己正在更新密钥以进行更改,则您的代码中的其他地方可能存在问题。使用Math.random()in 键将在每次渲染时重新创建元素。我不建议像这样更新密钥,因为 react 使用密钥来确定重新渲染事物的最佳方式。

@罗伯特格兰特,回头看,我同意。增加的复杂性并不值得。
2021-06-05 23:44:32
我很想知道为什么这会被否决?我一直在努力让屏幕阅读器响应 aria 警报,这是我发现的唯一可靠的技术。如果您的 UI 中的某些内容在每次单击时生成相同的警报,默认情况下 react 不会重新渲染 DOM,因此屏幕阅读器不会宣布更改。设置唯一键使其工作。也许反对是因为它仍然涉及设置状态。但是通过设置键来强制重新渲染到 DOM 是很重要的!
2021-06-08 23:44:32
@atomkirk 我和你的意见一样,但是,令人惊讶的是,在某些情况下,react 文档批准甚至推荐这种方法:reactjs.org/blog/2018/06/07/...
2021-06-14 23:44:32
@xtraSimplicity 我不能同意这符合 React 的做事方式。forceUpdate是这样做的反应方式。
2021-06-15 23:44:32
这是对key. 首先,其意图不明确。你的代码的读者必须努力理解你为什么使用key这种方式。其次,这并不比forceUpdate. “纯” React 意味着组件的视觉呈现 100% 依赖于其状态,因此如果您更改任何状态,它就会更新。但是,如果您有一些深度嵌套的对象(forceUpdate文档引用了使用它的原因的场景),那么 usingforceUpdate就清楚了。第三,Math.random()是……随机。理论上,它可以生成相同的随机数。
2021-06-16 23:44:32

其实,forceUpdate()是唯一正确的解决方案,setState()威力触发如果额外的逻辑中实现重新绘制shouldComponentUpdate()或当它只是回报false

强制性升级()

调用forceUpdate()将导致render()在组件上调用,跳过shouldComponentUpdate(). 更多的...

设置状态()

setState()除非在shouldComponentUpdate(). 更多的...


forceUpdate() 可以从您的组件中调用 this.forceUpdate()


Hooks:如何强制组件在 React 中使用钩子重新渲染?


顺便说一句:你是在改变状态还是你的嵌套属性不传播?

@lfender6445 直接this.state在构造函数外部分配状态也应该抛出错误。2016 年可能没有这样做;但是,我很确定它现在做到了。
2021-05-21 23:44:32
需要注意的一件有趣的事情是 setState 之外的状态赋值,例如 this.state.foo = 'bar' 不会触发渲染生命周期方法
2021-05-26 23:44:32
我添加了一些链接来解决直接状态分配。
2021-06-05 23:44:32

2021 年,这是强制更新 React 功能组件的官方方式

const [, forceUpdate] = useReducer(x => x + 1, 0);

  function handleClick() {
    forceUpdate();
  }

我知道 OP 是用于类组件的。但是这个问题是在 2015 年提出的,现在钩子可用,许多人可能会forceUpdate在功能组件中搜索这一点是给他们的。

请记住,它仍然说:“如果可能,尽量避免这种模式”。
2021-06-07 23:44:32
这应该从 2021 年开始发送到顶部。
2021-06-08 23:44:32
@John 是的,这个问题的所有答案都是如此,因为你不应该这样做forceUpdate如果您发现自己使用了强制更新的任何实现,这通常意味着您正在做一些不是 React 方式的事情。尽可能避免 forceUpdate,除非您必须这样做!
2021-06-13 23:44:32

forceUpdate通过以下方式避免

错误的方法:不要使用索引作为键

this.state.rows.map((item, index) =>
   <MyComponent cell={item} key={index} />
)

正确方法:使用数据 ID 作为键,它可以是一些 guid 等

this.state.rows.map((item) =>
   <MyComponent item={item} key={item.id} />
)

因此,通过进行此类代码改进,您的组件将是唯一的并自然呈现

太棒了。比使用随机数要好得多。
2021-05-22 23:44:32
this.state.rows.map((item) => <MyComponent item={item} key={item.id} /> )
2021-06-06 23:44:32
为了证明我的反对票是合理的,尽管使用这种方法是一种很好的做法,但这个答案与问题无关。
2021-06-13 23:44:32
非常感谢您将我推向正确的方向。使用索引作为键确实是我的问题。
2021-06-20 23:44:32