你可以强制React组件重新渲染而不调用setState吗?

时间:2015-06-03 16:44:58

标签: reactjs react-jsx

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

使用顶级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(),则可以正常使用。

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

20 个答案:

答案 0 :(得分:616)

在您的组件中,您可以致电this.forceUpdate()强制重新投降。

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

答案 1 :(得分:268)

应避免使用

forceUpdate,因为它偏离了React的心态。 React文档引用了可能使用forceUpdate的示例:

  

默认情况下,当组件的状态或道具发生更改时,组件将重新呈现。但是,如果这些隐式更改(例如:对象内部的数据更改而不更改对象本身)或者如果render()方法依赖于其他一些数据,则可以告诉React需要通过调用重新运行render() forceUpdate()。

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

  

通常你应该尽量避免使用forceUpdate(),只能在render()中读取this.props和this.state。这使您的组件“纯粹”,您的应用程序更简单,更高效。 https://facebook.github.io/react/docs/component-api.html#forceupdate

更改要重新渲染的元素的键将起作用。通过状态在元素上设置关键支柱,然后在您想要更新设置状态以获得新密钥时。

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

然后发生更改并重置密钥

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

我想要注意,这将替换密钥正在更改的元素。这可能有用的一个示例是当您有一个文件输入字段,您希望在图像上载后重置。

虽然OP问题的真正答案是forceUpdate()我发现这个解决方案在不同的情况下很有用。我还要注意,如果您发现自己使用forceUpdate,可能需要查看代码并查看是否有其他方法可以执行操作。

请注意1-9-2019:

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

答案 2 :(得分:53)

实际上,forceUpdate()是唯一正确的解决方案,因为setState()可能触发重新渲染,如果在shouldComponentUpdate()中实现了其他逻辑,或者只是返回false

forceUpdate()

  

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

的setState()

  除非在setState()中实现条件呈现逻辑,否则

shouldComponentUpdate()将始终触发重新渲染。 more...

<小时/> 可以通过forceUpdate()

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

答案 3 :(得分:30)

当您希望两个React组件进行通信时,它们不受关系(父子)的约束,建议使用Flux或类似的体系结构。

您要做的是监听 observable component 商店的更改,该商店保存模型及其界面,并保存导致呈现更改为state的数据在MyComponent。当商店推送新数据时,您可以更改组件的状态,从而自动触发渲染。

通常您应该尽量避免使用forceUpdate()。来自文档:

  

通常你应该尽量避免使用forceUpdate(),只能在render()中读取this.props和this.state。这使您的应用程序更简单,更高效

答案 4 :(得分:27)

我通过以下方式避免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} />
)

通过这样的代码改进,您的组件将 UNIQUE 并自然呈现

答案 5 :(得分:14)

在 2021 年,这是 the official way 强制更新 React 功能组件。

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

  function handleClick() {
    forceUpdate();
  }

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

答案 6 :(得分:12)

  

所以我想我的问题是:React组件需要有状态   为了重新奉献?有没有办法强制组件更新   要求不改变国家?

其他答案试图说明你的方法,但重点是你不应该。即使是改变关键的黑客解决方案也忽略了这一点。 React的力量放弃了控制手动管理什么东西应该渲染,而只是关于你自己应该如何映射输入。然后提供输入流。

如果你需要手动强制重新渲染,你几乎肯定不会做正确的事情。

答案 7 :(得分:10)

使用HOC

绑定商店更改

使用 HOC(高阶组件)模式,您可以包装React组件并在商店更改时自动更新。这是一种非常轻量级的方法,没有框架。

withStores HOC处理商店更新

10
11
11
11

如何使用

import React, { Component } from 'react'

export default function(/* store1, store2, store3... */) {
  const storeArgs = Array.prototype.slice.call(arguments)
  return function(WrappedComponent) {
    return class WithStore extends Component {
      constructor(props) {
        super(props)
        this.state = {
          lastUpdated: Date.now()
        }
        this.stores = storeArgs
      }

      _onChange = () => {
        this.setState({ lastUpdated: Date.now() })
      }

      componentWillMount = () => {
        this.stores && this.stores.forEach(store => {
          // each store has a common change event to subscribe to
          store.on('change', this._onChange)
        })
      }

      componentWillUnmount = () => {
        this.stores && this.stores.forEach(store => {
          store.off('change', this._onChange)
        })
      }

      render() {
        return <WrappedComponent 
                 lastUpdated={this.state.lastUpdated}
                 {...this.props}  />
      }
    }
  }
}

简单商店类示例

import React, { Component } from 'react'
import { View, Text, Button } from 'react-native'
import withStores from './withStores'
import userStore from './stores/users'

class MyView {
  _increaseUserCount = () => {
    userStore.setState({ userCount: userStore.state.count + 1 })
  }

  render() {
    return (
      <View>
        <Text>User count: {userStore.state.count}</Text>
        <Button
          title="Increase User Count"
          onPress={this._increaseUserCount}
        />
      </View>
    )
  }
}

return withStores(userStore)(MyView)

将实例存储为单身

var ee = require('event-emitter')
export default class Store {
  constructor() {
    this._state = {}
    this._eventEmitter = ee({})
  }
  get state() {
    return this._state
  }
  setState(newState) {
    this._state = {...this._state, ...newState}
    this._eventEmitter.emit('change')
  }
  on(ev, fn) {
    this._eventEmitter.on(ev, fn)
  }
  off(ev, fn) {
    this._eventEmitter.off(ev, fn)
  }
}

真正意味着小型项目,组件树深度较低。原因是如果你将它用于更大的组件树,这对于为商店更新的部分没有选择性,只是商店本身会触发更新。

如果您以更精细的方式拆分商店,那就没问题了,这就是 redux很好的原因,因为它可以根据需要进行细化,只需要一个商店但是在较小的项目上也有不成比例的样板。

答案 8 :(得分:4)

你可以通过以下两种方式实现:

<强> 1。使用forceUpdate()方法:

使用forceUpdate()方法时可能会出现一些故障。一个例子是它忽略了shouldComponentUpdate()方法,并且无论shouldComponentUpdate()是否返回false,都将重新呈现视图。因此,在可能的情况下应尽量避免使用forceUpdate()。

<强> 2。将this.state传递给setState()方法

以下代码行克服了上一个示例的问题:

this.setState(this.state);

真的这一切都是用触发重新渲染的当前状态覆盖当前状态。这仍然不是最好的做事方式,但它确实克服了使用forceUpdate()方法可能遇到的一些故障。

答案 9 :(得分:2)

我们可以如下使用this.forceUpdate()。

       class MyComponent extends React.Component {



      handleButtonClick = ()=>{
          this.forceUpdate();
     }


 render() {

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

 ReactDOM.render(<MyComponent /> , mountNode);

即使使用setState重新呈现组件,DOM中的元素'Math.random'部分也只会更新。

这里的所有答案都是对问题理解的正确补充。.众所周知,不使用setState({})即可重新渲染组件是通过使用forceUpdate()。

上面的代码与setState一起运行,如下所示。

 class MyComponent extends React.Component {



             handleButtonClick = ()=>{
                this.setState({ });
              }


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

ReactDOM.render(<MyComponent /> , mountNode);

答案 10 :(得分:1)

只需再回复一次即可备份已接受的答案:-)

React不鼓励使用forceUpdate(),因为它们通常在函数式编程中使用非常“这是唯一的方法”。在许多情况下这很好,但是许多React开发人员都带有面向对象的背景,并且采用这种方法,完全可以监听可观察对象。

如果这样做,您可能知道您必须在可观察到的“开火”时重新渲染,因此,您应该使用forceUpdate(),而这里不涉及shouldComponentUpdate()实际上是一个加法。

MobX之类的工具需要采用面向对象的方法,实际上是在表面之下进行此操作(实际上MobX直接调用render()

答案 11 :(得分:1)

有几种方法可以重新渲染组件:

最简单的解决方案是使用forceUpdate()方法:

this.forceUpdate()

另一种解决方案是在状态(nonUsedKey)中创建未使用的密钥  并通过更新此nonUsedKey来调用setState函数:

this.setState({ nonUsedKey: Date.now() } );

或重写所有当前状态:

this.setState(this.state);

更改道具还可以重新渲染组件。

答案 12 :(得分:1)

为完整起见,您还可以在功能组件中实现此目的:

const [, updateState] = useState();
const forceUpdate = useCallback(() => updateState({}), []);
// ...
forceUpdate();

或者,作为可重复使用的钩子:

const useForceUpdate = () => {
  const [, updateState] = useState();
  return useCallback(() => updateState({}), []);
}
// const forceUpdate = useForceUpdate();

请参阅:https://stackoverflow.com/a/53215514/2692307

请注意,使用强制更新机制仍然是不正确的做法,因为它违背了反应的心态,因此,如果可能的话,仍应避免使用它。

答案 13 :(得分:0)

我发现最好避免使用forceUpdate()。强制重新渲染的一种方法是在临时外部变量上添加render()的依赖关系,并在需要时更改该变量的值。

这是一个代码示例:

class Example extends Component{
   constructor(props){
      this.state = {temp:0};

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

   forceChange(){
      this.setState(prevState => ({
          temp: prevState.temp++
      })); 
   }

   render(){
      return(
         <div>{this.state.temp &&
             <div>
                  ... add code here ...
             </div>}
         </div>
      )
   }
}

当您需要强制重新渲染时调用this.forceChange()。

答案 14 :(得分:0)

ES6 - 我举了一个例子,这对我有帮助:

在“短if语句”中,您可以传递如下的空函数:

isReady ? ()=>{} : onClick

这似乎是最短的方法。

()=>{}

答案 15 :(得分:0)

另一种方法是调用setState AND 保存状态:

this.setState(prevState=>({...prevState}));

答案 16 :(得分:0)

forceUpdate();方法可以使用,但建议使用setState();

答案 17 :(得分:0)

为了完成您要描述的内容,请尝试this.forceUpdate()。

答案 18 :(得分:0)

您可以使用forceUpdate()获得更多详细信息,请检查https://reactjs.org/docs/react-component.html#forceupdate

答案 19 :(得分:0)

forceUpdate(),但是每次我听到有人谈论它时,都应该跟进它,永远不要使用它。