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

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

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

  • 4
    В принятом ответе говорится, что this.forceUpdate() является правильным решением, тогда как остальные ответы и несколько комментариев против использования forceUpdate() . Тогда будет ли хорошо сказать, что вопрос еще не получил правильного решения / ответа?
  • 3
    Исключенный ответ ответил на мой вопрос в то время. Технически это ответ, который я искал, и я все еще думаю правильный ответ. Другие ответы, я думаю, являются хорошей дополнительной информацией для людей с таким же вопросом, о которых нужно знать.
Показать ещё 1 комментарий
Теги:
react-jsx

15 ответов

507
Лучший ответ

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

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

  • 114
    Другой способ - это this.setState (this.state);
  • 14
    Использование forceupdate не рекомендуется, смотрите последний ответ.
Показать ещё 7 комментариев
221

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

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

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

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

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

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

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

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

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

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

  • 11
    Мне было бы интересно узнать, почему это получило понижательную оценку? Я бился головой, пытаясь заставить программы чтения с экрана реагировать на предупреждения арии, и это единственная найденная мною техника, которая работает надежно. Если в вашем пользовательском интерфейсе есть что-то, генерирующее одно и то же предупреждение при каждом нажатии, по умолчанию реакция не выполняет повторную визуализацию DOM, поэтому программа чтения с экрана не объявляет об изменении. Установка уникального ключа заставляет его работать. Может быть, это было связано с тем, что оно все еще подразумевает настройку состояния Но форсирование повторного рендеринга в DOM путем установки ключа является золотым!
  • 2
    Мне очень понравился этот ответ, потому что - 1: использование forceupdate () не рекомендуется, и 2: этот способ очень полезен для сторонних компонентов, которые вы установили через npm, то есть не для ваших собственных компонентов. Например, React-resizable-and-movable - это сторонний компонент, который я использую, но он не реагирует на состояние моего компонента. это отличное решение.
Показать ещё 14 комментариев
46

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

forceUpdate()

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

SetState()

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


forceUpdate() можно вызвать из вашего компонента с помощью this.forceUpdate()
  • 0
    Одна интересная вещь, на которую следует обратить внимание, это то, что изменения состояния вне setState, такие как this.state.foo = 'bar', не будут вызывать метод жизненного цикла рендеринга.
  • 3
    @ lfender6445 Присваивание состояния напрямую с помощью this.state вне конструктора также должно this.state ошибку. Может быть, не сделали этого в 2016 году; но я уверен, что теперь это так.
27

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

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

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

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

  • 2
    Каков статус памяти состояния компонента? Если у меня есть пять компонентов на странице, и все они слушают одно хранилище, у меня есть данные в памяти пять раз, или это ссылки? И разве все эти слушатели не складывают быстро? Почему лучше «просачиваться», чем просто передавать данные вашей цели?
  • 2
    На самом деле, рекомендации состоят в том, чтобы передавать данные хранилища в реквизиты компонентов и использовать состояние компонента только для таких вещей, как состояние прокрутки и другие незначительные специфические для пользовательского интерфейса вещи. Если вы используете redux (я рекомендую его), вы можете использовать функцию connect из react-redux для автоматической привязки состояния магазина к реквизитам компонентов, когда это необходимо, на основе предоставленной вами функции отображения.
Показать ещё 3 комментария
21

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

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

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

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

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

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

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

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

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

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

  • 0
    @ art-solopov, о какой проблеме ты говоришь? Какие документы представляют их? И, предполагая, что вы ссылаетесь на вложенные объекты в состоянии, ответ заключается в использовании другой структуры для хранения вашего состояния. Это явно неоптимальный способ обработки состояния в React. Вы также не должны управлять государством отдельно. Вы теряете одно из самых больших преимуществ React, если не работаете с системой государственного управления. Управление обновлениями вручную является анти-паттерном.
7

Ни одно государство не заботится о том, чтобы перерисовать

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

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

  • 2
    Я думаю, что в вашем примере класса Store в методе update есть опечатка, в которой нужно написать первую строку метода следующим образом: this._data = {...this._data, ...newData} .
  • 1
    @ Норберт спасибо и исправил, удаман.
Показать ещё 3 комментария
6

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

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

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

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

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

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

this.setState(this.state);

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

  • 2
    Поскольку setState является пакетным, возможно, это безопаснее: this.setState(prevState => prevState);
  • 0
    Не совсем уверен, почему это «глюк». Имя метода ('forceUpdate') не может быть более понятным: принудительное обновление всегда.
0

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

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

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

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

0

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

  • 0
    Что делает это действие?
0

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

0

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

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

0

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

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

isReady ? ()=>{} : onClick

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

()=>{}
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(), когда вам нужно выполнить принудительный повторный рендеринг.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню