首页 文章

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

提问于
浏览
431

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

使用顶级 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() ,它可以正常工作 .

So I guess my question is: 做反应组件需要有状态才能重新渲染吗?有没有办法强制组件按需更新而不改变状态?

15 回答

  • 0

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

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

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

  • 0

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

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

  • 6

    我发现最好避免使用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() .

  • 22

    没有州担心重新渲染

    当我开始使用反应时,我正在将我的数据存储在状态中,我必须这样做才能进行渲染 . 那是非常错误的 . 事实证明,如果你推出一些简单的不可变商店,你甚至可以避免做像Flux和Redux这样复杂的事情 .

    BaseView 类继承和处理商店更新

    import React, { Component } from 'react';
    import { View } from 'react-native';
    
    export default class BaseView extends Component {
    
      constructor(props) {
        super(props)
        this.state = {}
      }
    
      onChange = () => {
        this.setState(this.state) // dumb easy: triggers render
      }
    
      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)
        })
      }
    
    }
    

    How to use

    import React, { Component } from 'react'
    import { View, Text } from 'react-native'
    import BaseView from './BaseView'
    import myStore from './myStore'
    
    class MyView extends BaseView {
      contructor(props) {
        super(props)
        this.stores = [myStore]
      }
    
      render() {
        return (<View><Text onPress={() => {
          myStore.update({ message: 'hi' + Date.now() })
        }}>{myStore.get().message}</Text></View>)
      }
    }
    

    简单的 Store 类示例

    var ee = require('event-emitter')
    export default class Store {
      constructor() {
        this._data = {}
        this._eventEmitter = ee({})
      }
      get() {
        return {...this._data} // immutable
      }
      update(newData) {
        this._data = {..._this.data, ...newData}
        this._eventEmitter.emit('change')
      }
      on(ev, fn) {
        this._eventEmitter.on(ev, fn)
      }
      off(ev, fn) {
        this._eventEmitter.off(ev, fn)
      }
    }
    

    Store instance 作为单身人士

    import Store from './Store'
    
    const myStore = new Store()
    export default myStore
    

    我认为这消除了较小应用程序所需的所有苛刻框架 . 尽管与Immutable.js结合使用,还可以将redux用于中到大的 .

  • 27

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

  • 46

    如果希望两个React组件进行通信(不受关系(父子)约束),建议使用Flux或类似的体系结构 .

    您要做的是监听可观察组件存储的更改,该组件存储保存模型及其接口,并保存导致渲染更改为 MyComponent 中的 state 的数据 . 当商店推送新数据时,您可以更改组件的状态,从而自动触发渲染 .

    通常你应该尽量避免使用 forceUpdate() . 从文档:

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

  • 0

    应避免使用 forceUpdate ,因为它偏离了React的思维模式 . React文档引用了一个可能使用 forceUpdate 的示例:

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

    但是,我想提出一个想法,即使使用深度嵌套的对象, 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 ,您可能需要查看代码并查看是否有其他方法可以执行操作 .

  • 532

    我通过执行以下操作避免了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 并自然渲染

  • 232

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

  • 0

    你可以通过以下两种方式做到:

    1. Use the forceUpdate() method:

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

    2. Passing this.state to the setState() method

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

    this.setState(this.state);
    

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

  • 0

    ES6 - 我提供了一个例子,对我有帮助:

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

    isReady ? ()=>{} : onClick
    

    这似乎是最短的方法 .

    ()=>{}
    
  • 11

    在您的组件中,您可以调用 this.forceUpdate() 强制重新呈现 .

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

  • 3

    我们可以使用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中的Element'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);
    
  • 0

    只是另一个回复备份接受的答案:-)

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

    如果你这样做,你可能知道你必须重新渲染observable "fires",因此,你应该使用 forceUpdate() ,它实际上是一个加号 shouldComponentUpdate() 不参与此处 .

    像MobX这样采用OO方法的工具实际上是在表面下执行此操作(实际上MobX直接调用 render()

  • 6

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

    forceUpdate()

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

    setState()

    除非在shouldComponentUpdate()中实现条件呈现逻辑,否则setState()将始终触发重新呈现 . 更多...

    可以通过 this.forceUpdate() 从组件中调用

    forceUpdate()

相关问题