Можете ли вы заставить компонент 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(), он отлично работает.

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

ПРИМЕЧАНИЕ 1-9-2019:

Выше (изменение ключа) полностью заменит элемент. Если вы обнаружите, что обновляете ключ для внесения изменений, возможно, у вас есть проблема где-то еще в вашем коде. Использование Math.random() в ключе создаст заново элемент с каждым рендером. Я НЕ рекомендовал бы обновлять ключ, как это, так как реагирует использует ключ, чтобы определить лучший способ перерисовать вещи.

Ответ 3

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

forceUpdate()

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

SetState()

setState() всегда вызывает повторную визуализацию, если в shouldComponentUpdate() не реализована логика условного воспроизведения. подробнее...


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

Ответ 4

Я forceUpdate, выполнив следующие

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

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

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

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

таким образом, улучшая код, ваш компонент будет УНИКАЛЬНЫМ и будет отображаться естественным образом.

Ответ 5

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

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

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

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

Ответ 6

Связать изменения магазина с помощью HOC

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

withStores HOC обрабатывает обновления магазина

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

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

Сохранить экземпляр как синглтон

import Store from './Store'

const myStore = new Store()
export default myStore

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

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

Ответ 7

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

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

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

Ответ 8

Вы можете сделать это несколькими способами:

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

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

2. Передача this.state в метод setState()

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

this.setState(this.state);

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

Ответ 9

Есть несколько способов перерисовки вашего компонента:

Самое простое решение - использовать метод forceUpdate():

this.forceUpdate()

Еще одно решение - создать неиспользуемый ключ в состоянии (nonUsedKey) и вызвать функцию setState с обновлением этого nonUsedKey:

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

Или переписать все текущее состояние:

this.setState(this.state);

Изменение реквизита также обеспечивает повторное отображение компонента.

Ответ 10

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

Элемент "Math.random" элемента в 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);

Ответ 11

Просто еще один ответ для подтверждения принятого ответа :-)

React не рекомендует использовать forceUpdate() потому что у них, как правило, очень функциональный подход "это единственный способ сделать это". Это хорошо во многих случаях, но многие разработчики React приходят с OO-фоном, и при таком подходе совершенно нормально слушать наблюдаемый объект.

И если вы это сделаете, вы, вероятно, знаете, что вы ДОЛЖНЫ сделать повторный рендеринг, когда наблюдаемое "срабатывает", и, как так, вы ДОЛЖНЫ использовать forceUpdate() и это на самом деле плюс, который не shouldComponentUpdate() быть здесь shouldComponentUpdate().

Такие инструменты, как MobX, который использует OO-подход, фактически делают это под поверхностью (на самом деле MobX вызывает render() напрямую)

Ответ 12

Я нашел, что лучше избегать 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(), когда вам нужно выполнить принудительный повторный рендеринг.

Ответ 13

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

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

isReady ? ()=>{} : onClick

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

()=>{}

Ответ 14

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

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

Ответ 15

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

Ответ 16

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

Ответ 17

Вы можете использовать forceUpdate() для более подробной проверки (forceUpdate()).

Ответ 18

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