Можете ли вы заставить компонент React повторно выполнить вызов без вызова setState?

у меня есть внешний (для компонента), наблюдаемый объект, который я хочу слушать для изменений. Когда объект обновляется, он испускает события изменения, а затем я хочу перезапустить компонент при обнаружении любого изменения.

С 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(), он работает нормально.

Итак, я думаю, мой вопрос: реагируют компоненты нужно иметь государство для того, чтобы перепродать? Есть ли способ заставить компонент обновляться по требованию без изменения состояния?

14 ответов


в вашем компоненте вы можете вызвать this.forceUpdate() для принудительного повторения.

документация:https://facebook.github.io/react/docs/component-api.html


forceUpdate следует избегать, потому что он отклоняется от реактивного мышления. В документах React приведен пример when forceUpdate могут быть использованы:

по умолчанию, когда состояние или реквизит вашего компонента изменяется, ваш компонент будет повторно визуализироваться. Однако, если они изменяются неявно (например: данные в глубине объекта меняются без изменения самого объекта) или если ваш метод render() зависит от некоторых других данных, вы можете сказать React, что ему нужно повторно запустить render (), вызвав forceUpdate().

однако я хотел бы предложить идею, что даже с глубоко вложенными объектами,forceUpdate ненужно. При использовании неизменяемого источника данных отслеживание изменений становится дешевым; изменение всегда приведет к новому объекту, поэтому нам нужно только проверить, изменилась ли ссылка на объект. Вы можете использовать библиотеку неизменяемый JS для реализации неизменяемых объектов данных в приложении.

обычно вы должны стараться избегать всех видов использования forceUpdate () и только читать из этого.реквизит и это.состояние в render (). Это делает ваш компонент "чистым", а ваше приложение намного проще и эффективнее. https://facebook.github.io/react/docs/component-api.html#forceupdate

изменение ключа элемента, который вы хотите повторно визуализировать, будет работать. Установите ключевую опору на свой элемент через состояние, а затем, когда вы хотите обновить состояние set, чтобы иметь новый ключ.

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

затем происходит изменение и сброс ключа

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

Я хочу отметить, что это заменит элемент, который ключ меняется. Пример того, где это может быть полезно, когда у вас есть поле ввода файла, которое вы хотите сбросить после загрузки изображения.

в то время как истинный ответ на вопрос OP будет forceUpdate() я нашел это решение полезным в разных ситуациях. Я также хочу отметить, что если вы используете forceUpdate вы можете просмотреть свой код и посмотреть, если есть другой способ делать вещи.


на самом деле forceUpdate() является единственным правильным решением, как setState() might не вызвать повторную визуализацию, если дополнительная логика реализована в shouldComponentUpdate() или когда он просто возвращает false.

forceUpdate()

вызов forceUpdate() вызывает render() для вызова компонента, пропуская shouldComponentUpdate(). больше...

выполнении функция setState()

setState() всегда будет вызывать рендеринг, если логика условного рендеринга не реализована в shouldComponentUpdate(). больше...


forceUpdate() можно вызвать из вашего компонента с помощью this.forceUpdate()

когда вы хотите, чтобы два компонента React общались, которые не связаны отношениями (родитель-ребенок), рекомендуется использовать Flux или подобных архитектур.

что вы хотите сделать, это слушать для изменения наблюдаемых компонент store, который содержит модель и ее интерфейс, и сохранение данных, которые вызывают изменение рендеринга как state на MyComponent. Когда хранилище выталкивает новые данные, изменяется состояние компонента, который автоматически запускает рендеринг.

обычно вы должны стараться избегать использования forceUpdate() . Из документации:

обычно вы должны стараться избегать всех видов использования forceUpdate () и читать только из этого.реквизит и это.состояние в render (). Это делает ваше приложение намного проще и эффективнее


Я Избегал forceUpdate, выполнив следующие

НЕВЕРНЫЙ ПУТЬ: не используйте индекс в качестве ключа

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

ПРАВИЛЬНО: используйте идентификатор данных в качестве ключа, это может быть какой-то guid и т. д.

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

чтобы делать такие улучшения кода будет уникальный и визуализации естественным образом


поэтому я думаю, что мой вопрос: Должны ли компоненты React иметь состояние в заказать повторно? Есть ли способ заставить компонент обновляться спрос без изменения состояния?

другие ответы пытались проиллюстрировать, как вы могли бы, но дело в том, что не стоит. Даже избитое решение об изменении ключа упускает суть. Сила React-это отказ от контроля ручного управления, когда что-то должно отображаться, и вместо этого просто о том, как что-то должно отображаться на входах. Затем подайте поток входов.

Если вам нужно вручную принудительно перерисовывать, вы почти наверняка не делаете что-то правильно.


нет государственных забот для повторного рендеринга

Я много хранил свои данные в состоянии, когда начал использовать react, думая, что мне нужно сделать это для рендеринга. Это было очень неправильно. Оказывается, вы даже можете избежать сложных вещей, таких как 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)
    })
  }

}

как использовать

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>)
  }
}

простой магазине пример класс

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)
  }
}

хранилище как синглтон

import Store from './Store'

const myStore = new Store()
export default myStore

Я думаю, что это устраняет все crufty рамки, необходимые для небольших приложений. Используйте redux для средних и больших, хотя в сочетании с неизменяемым.js.


мы можем использовать это.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);

Элемент 'Математика.случайная часть в DOM обновляется только при использовании setState для повторного отображения компонента.

все ответы здесь правильные, дополняющие вопрос для понимания..как мы знаем, чтобы повторно отобразить компонент с помощью 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);

вы могли бы сделать это двумя способами:

1. Используйте forceUpdate() способ:

есть некоторые глюки, которые могут произойти при использовании forceUpdate() метод. Одним из примеров является то, что он игнорирует shouldComponentUpdate() способ и ре-генерации независимо от того,shouldComponentUpdate() возвращает false. Из-за этого следует избегать использования forceUpdate (), когда это вообще возможно.

2. Передаю это.состояние до setState() метод

в следующая строка кода преодолевает проблему с предыдущим примером:

this.setState(this.state);

на самом деле все это делает перезапись текущего состояния с текущим состоянием, которое вызывает повторный рендеринг. Это все еще не обязательно лучший способ сделать что-то, но он преодолевает некоторые сбои, с которыми вы можете столкнуться, используя метод forceUpdate ().


Я нашел, что лучше всего избегать 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>
      )
   }
}

назвать это.forceChange () когда вам нужно принудительно повторно отобразить.


ES6-я включаю пример, который был полезен для меня:

в операторе "short if" вы можете передать пустую функцию следующим образом:

isReady ? ()=>{} : onClick

Это, кажется, самый короткий подход.

()=>{}

другой способ-вызов setState, и сохранять состояние:

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


forceUpdate(); метод будет работать, но желательно использовать setState();


чтобы выполнить то, что вы описываете, пожалуйста, попробуйте это.forceUpdate().