Можете ли вы принудительно выполнить повторную визуализацию компонента React, не вызывая setState?

avatar
Philip Walton
3 июня 2015 в 16:44
1171801
21
894

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

Для верхнего уровня 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(), он будет работать нормально.

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

Источник
Adil
14 сентября 2017 в 12:39
10

в принятом ответе говорится, что this.forceUpdate() является правильным решением, тогда как остальные ответы и несколько комментариев против использования forceUpdate(). Можно ли тогда сказать, что вопрос еще не получил правильного решения / ответа?

Philip Walton
17 сентября 2017 в 18:36
9

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

King Friday
23 ноября 2017 в 18:08
0

Интересно отметить, что вам вообще НИЧЕГО НЕ НУЖНО В СОСТОЯНИИ, кроме инициализации его простым объектом, а затем вызов this.setState ({}) просто запускает новый рендеринг. React - это здорово, но иногда и странно. Таким образом, вы можете напрямую перебирать данные хранилища при запуске изменения, не обращая внимания на данные для каждого экземпляра компонента.

Daniel
27 ноября 2019 в 19:30
0

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

Ответы (21)

avatar
Crob
3 июня 2015 в 16:47
940

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

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

В функциональных компонентах нет эквивалента forceUpdate, но вы можете придумать способ принудительного обновления с помощью ловушки useState.

kar
7 февраля 2016 в 08:56
214

Другой способ - this.setState (this.state);

Varand Pezeshkian
4 апреля 2016 в 20:33
27

Не рекомендуется использовать forceupdate, см. Последний ответ.

ArneHugo
24 мая 2016 в 19:11
48

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

Qwerty
2 июня 2016 в 14:03
8

@kar На самом деле, в shouldComponentUpdate() может быть реализована дополнительная логика, что сделает ваше решение бесполезным.

IntoTheDeep
24 августа 2016 в 18:39
4

Превышен максимальный размер стека вызовов

zulkarnain shah
28 марта 2017 в 15:26
1

Ошибка: возможное отклонение необработанного обещания (id: 0): _this2.forceUpdate не является функцией

Jason McCarrell
11 апреля 2017 в 21:10
0

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

Jason McCarrell
11 апреля 2017 в 21:12
0

@zulkarnainshah функциональный компонент без сохранения состояния не имеет прикрепленного слова this. Если вы хотите использовать forceUpdate, вам нужно превратить свой компонент в класс.

Manish
28 декабря 2017 в 19:21
0

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

Si8
17 июля 2019 в 16:27
0

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

KMA Badshah
30 ноября 2020 в 08:05
0

setState() асинхронный. И поэтому в некоторых случаях это работает не очень хорошо.

xyres
9 мая 2021 в 18:57
0

@JasonMcCarrell Нет, в некоторых случаях forceUpdate() полезно. Если бы это было действительно так плохо, это вообще не было бы частью API.

avatar
Raskul
20 ноября 2021 в 06:27
0

используйте useEffect как смесь componentDidMount, componentDidUpdate и componentWillUnmount, как указано в документации React.

Чтобы вести себя как componentDidMount, вам нужно настроить useEffect следующим образом:

  useEffect(() => console.log('mounted'), []);

Первый аргумент - это обратный вызов, который будет запущен на основе второго аргумента, который представляет собой массив значений. Если любое из значений этого второго аргумента изменится, функция обратного вызова, которую вы определили в своем useEffect, будет запущена.

Однако в примере, который я показываю, я передаю пустой массив в качестве второго аргумента, и он никогда не будет изменен, поэтому функция обратного вызова будет вызываться один раз при монтировании компонента.

Это резюмирует useEffect. Если вместо пустого значения у вас есть аргумент, например:

 useEffect(() => {

  }, [props.lang]);

Это означает, что каждый раз при изменении props.lang будет вызываться ваша функция обратного вызова. useEffect на самом деле не будет повторно отображать ваш компонент, если вы не управляете каким-либо состоянием внутри этой функции обратного вызова, которое может запускать повторную визуализацию.

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

Например, здесь функция рендеринга начинается с отображения английского языка в качестве языка по умолчанию, и в моем эффекте использования я меняю этот язык через 3 секунды, поэтому рендеринг перерисовывается и начинает показывать «испанский».

function App() {
  const [lang, setLang] = useState("english");

  useEffect(() => {
    setTimeout(() => {
      setLang("spanish");
    }, 3000);
  }, []);

  return (
    <div className="App">
      <h1>Lang:</h1>
      <p>{lang}</p>
    </div>
  );
}
avatar
Tom Bombadil
2 марта 2021 в 09:15
57

В 2021 году это официальный способ принудительного обновления функционального компонента React.

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

  function handleClick() {
    forceUpdate();
  }

Я знаю, что OP предназначен для компонента класса. Но этот вопрос был задан в 2015 году, и теперь, когда доступны хуки, многие могут искать forceUpdate в функциональных компонентах. Это немного для них.

Ziwon
23 августа 2021 в 03:41
3

Это должно быть отправлено наверх до 2021 года и далее.

John
7 ноября 2021 в 00:52
0

Имейте в виду, что по-прежнему говорится: «По возможности старайтесь избегать этого шаблона».

Tom Bombadil
7 ноября 2021 в 15:34
0

@John Ага, это так для всех ответов на этот вопрос, потому что вы не должны forceUpdate. Если вы обнаружите, что используете какую-либо из реализаций принудительного обновления, это обычно означает, что вы делаете что-то не так, как в React. По возможности избегайте forceUpdate, если только в этом нет необходимости!

avatar
Lukas Bach
22 октября 2019 в 21:32
5

Для полноты вы также можете добиться этого в функциональных компонентах:

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

Или, как многоразовый крючок:

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

См .: https://coderhelper.com/a/53215514/2692307

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

avatar
Ian
8 июля 2019 в 00:51
0

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

avatar
Harshit Singhai
2 июля 2019 в 18:16
-5

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

Byron Coetsee
17 апреля 2020 в 18:45
4

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

avatar
Jackkobec
19 марта 2019 в 13:55
7

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

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

this.forceUpdate()

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

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

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

this.setState(this.state);

Смена реквизита также обеспечивает повторную визуализацию компонентов.

avatar
Plaul
1 ноября 2018 в 09:44
1

Еще один ответ, подтверждающий принятый ответ :-)

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

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

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

avatar
API-Andy
18 сентября 2018 в 14:17
0

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

Dominique
18 сентября 2018 в 14:22
0

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

avatar
Chiamaka Ikeanyi
21 августа 2018 в 15:07
0
Метод

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

avatar
dvvtms
24 июля 2018 в 11:54
0

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

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

avatar
Dhana
6 июля 2018 в 13:10
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);

Элемент "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);
avatar
MrDoda
30 мая 2018 в 18:52
0

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

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

isReady ? ()=>{} : onClick

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

()=>{}
avatar
raksheetbhat
26 декабря 2017 в 06:12
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 (), когда вам нужно принудительно выполнить повторный рендеринг.

avatar
King Friday
23 ноября 2017 в 18:36
22

используйте крючки или HOC выбирайте

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

useStore Hooks способ обработки обновлений магазина

interface ISimpleStore {
  on: (ev: string, fn: () => void) => void;
  off: (ev: string, fn: () => void) => void;
}

export default function useStore<T extends ISimpleStore>(store: T) {
  const [storeState, setStoreState] = useState({store});
  useEffect(() => {
    const onChange = () => {
      setStoreState({store});
    }
    store.on('change', onChange);
    return () => {
      store.off('change', onChange);
    }
  }, []);
  return storeState.store;
}

withStores Обновления хранилища дескрипторов HOC

export default function (...stores: SimpleStore[]) {
  return function (WrappedComponent: React.ComponentType<any>) {
    return class WithStore extends PureComponent<{}, {lastUpdated: number}> {
      constructor(props: React.ComponentProps<any>) {
        super(props);
        this.state = {
          lastUpdated: Date.now(),
        };
        this.stores = stores;
      }

      private stores?: SimpleStore[];

      private onChange = () => {
        this.setState({lastUpdated: Date.now()});
      };

      componentDidMount = () => {
        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}
          />
        );
      }
    };
  };
}

Класс SimpleStore

import AsyncStorage from '@react-native-community/async-storage';
import ee, {Emitter} from 'event-emitter';

interface SimpleStoreArgs {
  key?: string;
  defaultState?: {[key: string]: any};
}

export default class SimpleStore {
  constructor({key, defaultState}: SimpleStoreArgs) {
    if (key) {
      this.key = key;
      // hydrate here if you want w/ localState or AsyncStorage
    }
    if (defaultState) {
      this._state = {...defaultState, loaded: false};
    } else {
      this._state = {loaded: true};
    }
  }
  protected key: string = '';
  protected _state: {[key: string]: any} = {};
  protected eventEmitter: Emitter = ee({});
  public setState(newState: {[key: string]: any}) {
    this._state = {...this._state, ...newState};
    this.eventEmitter.emit('change');
    if (this.key) {
      // store on client w/ localState or AsyncStorage
    }
  }
  public get state() {
    return this._state;
  }
  public on(ev: string, fn:() => void) {
    this.eventEmitter.on(ev, fn);
  }
  public off(ev: string, fn:() => void) {
    this.eventEmitter.off(ev, fn);
  }
  public get loaded(): boolean {
    return !!this._state.loaded;
  }
}

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

В случае крючков:

// use inside function like so
const someState = useStore(myStore);
someState.myProp = 'something';

В случае HOC:

// inside your code get/set your store and stuff just updates
const val = myStore.myProp;
myOtherStore.myProp = 'something';
// return your wrapped component like so
export default withStores(myStore)(MyComponent);

УБЕДИТЕСЬ Чтобы экспортировать ваши магазины как одноэлементные, чтобы воспользоваться преимуществами глобальных изменений, например:

class MyStore extends SimpleStore {
  public get someProp() {
    return this._state.someProp || '';
  }
  public set someProp(value: string) {
    this.setState({...this._state, someProp: value});
  }
}
// this is a singleton
const myStore = new MyStore();
export {myStore};

Этот подход довольно прост и мне подходит. Я также работаю в больших командах и использую Redux и MobX, и считаю, что они тоже хороши, но представляют собой много шаблонов. Мне лично нравится мой собственный подход, потому что я всегда ненавидел много кода для чего-то, что может быть простым, когда вам это нужно.

Norbert
12 декабря 2018 в 11:09
2

Я думаю, что в вашем примере класса Store есть опечатка в методе обновления, в котором нужно написать первую строку метода следующим образом: this._data = {...this._data, ...newData}.

Fred
8 февраля 2019 в 13:57
2

React препятствует наследованию в пользу композиции. reactjs.org/docs/composition-vs-inheritance.html

Zoman
13 февраля 2019 в 22:56
1

Просто интересно, что такое ясность / читабельность, как this.setState (this.state) может быть лучше, чем this.forceUpdate ()?

avatar
kojow7
1 сентября 2017 в 23:41
4

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

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

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

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

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

this.setState(this.state);

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

Mark Galloway
14 сентября 2017 в 19:15
2

Поскольку setState является пакетным, это, вероятно, безопаснее: this.setState(prevState => prevState);

Zoman
13 февраля 2019 в 23:02
1

Не совсем уверен, почему это «глюк». Название метода ('forceUpdate') как нельзя более ясное: принудительное обновление всегда.

avatar
vijay
6 августа 2017 в 04:42
47

Я избежал forceUpdate, выполнив следующие действия

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

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

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

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

, поэтому при таком улучшении кода ваш компонент будет UNIQUE и будет отображаться естественно

Tal
26 августа 2018 в 15:21
0

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

RoiEX
30 апреля 2019 в 08:52
0

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

micnic
17 октября 2019 в 13:31
2

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

Peter Moore
30 апреля 2021 в 21:31
0

Это потрясающе. Намного лучше, чем использовать случайное число.

avatar
Kyle Baker
8 сентября 2016 в 16:42
19

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

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

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

Kyle Baker
18 января 2017 в 21:25
0

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

HuLu ViCa
17 апреля 2020 в 17:28
0

Пожалуйста, ответьте на этот вопрос coderhelper.com/questions/61277292/…?

avatar
Qwerty
2 июня 2016 в 14:00
111

Фактически, forceUpdate() - единственное правильное решение, поскольку setState() может не вызвать повторный рендеринг, если дополнительная логика реализована в shouldComponentUpdate() или когда он просто возвращает false. >

forceUpdate ()

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

setState ()

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


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


Хуки: Как я могу принудительно выполнить повторный рендеринг компонента с помощью хуков в React?


Кстати: вы меняете состояние или ваши вложенные свойства не распространяются?

  • Как обновить свойства вложенного состояния в React
  • Песочница

lfender6445
26 декабря 2016 в 18:36
1

Следует отметить одну интересную вещь: присвоение состояний за пределами setState, например this.state.foo = 'bar', не будет запускать метод жизненного цикла рендеринга

Tyler
29 июня 2018 в 17:27
4

@ lfender6445 Непосредственное присвоение состояния с помощью this.state вне конструктора также должно вызывать ошибку. Возможно, в 2016 году этого не произошло; но я почти уверен, что он делает это сейчас.

Qwerty
20 мая 2020 в 15:23
0

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

avatar
pizzarob
25 января 2016 в 23:58
405

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

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

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

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

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

Martin Bayly
4 марта 2016 в 01:35
17

Мне было бы интересно узнать, почему за это проголосовали против? Я бился головой, пытаясь заставить программы чтения с экрана реагировать на предупреждения об арии, и это единственный метод, который я нашел, который работает надежно. Если у вас есть что-то в вашем пользовательском интерфейсе, которое генерирует одно и то же предупреждение при каждом нажатии, по умолчанию response не повторно отображает DOM, поэтому программа чтения с экрана не объявляет об изменении. Установка уникального ключа заставляет его работать. Возможно, отрицательный голос был вызван тем, что он все еще включает настройку состояния. Но принудительный повторный рендеринг в DOM с помощью установки ключа - это просто золото!

Varand Pezeshkian
4 апреля 2016 в 20:32
2

Мне очень понравился этот ответ, потому что - 1: использование forceupdate () не рекомендуется и 2: этот способ очень полезен для сторонних компонентов, которые вы установили через npm, то есть не для ваших собственных компонентов. Например, React-resizable-and-movable - это сторонний компонент, который я использую, но он не реагирует на мое состояние набора компонентов. это отличное решение.

Qwerty
2 июня 2016 в 14:02
0

При изменении ключа не удаляются старые узлы HTML и не создаются новые?

Qwerty
2 июня 2016 в 14:04
0

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

Izkata
13 июня 2016 в 18:20
0

@Qwerty key позволяет переупорядочивать узлы для скорости (пропуская удаление / добавление), но AFAIK, если нет кандидата, он изменит существующий узел вместо полного удаления / добавления

Izkata
13 июня 2016 в 18:22
0

@Qwerty Но да, использование key не подходит для этого примера, поскольку OP просто выводил значение, не делая ничего подобного

atomkirk
7 июля 2016 в 13:33
101

Это взлом и злоупотребление key. Во-первых, неясно его намерение. Читателю вашего кода придется потрудиться, чтобы понять, почему вы используете key таким образом. Во-вторых, это не более чисто, чем forceUpdate. «Чистый» React означает, что визуальное представление вашего компонента на 100% зависит от его состояния, поэтому, если вы измените какое-либо состояние, оно обновится. Однако, если у вас есть несколько глубоко вложенных объектов (сценарий, в котором forceUpdate приводятся причины для его использования), то использование forceUpdate проясняет это. В-третьих, Math.random() ... случайный. Теоретически он может генерировать такое же случайное число.

Rob Grant
25 февраля 2017 в 10:17
9

@xtraSimplicity Я не могу согласиться с тем, что это соответствует способу работы React. forceUpdate - это способ React.

XtraSimplicity
25 февраля 2017 в 10:26
3

@ Роберт Грант, оглядываясь назад, я согласен. Дополнительная сложность того не стоит.

James Wang
26 мая 2017 в 17:37
1

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

pizzarob
26 мая 2017 в 20:32
0

Прошло некоторое время с тех пор, как я ответил на этот вопрос. Единственный случай, когда мне нужно было рандомизировать ключ в последнее время, было использование загрузчика горячего модуля бета-реакции, но это было больше похоже на взлом, чем на что-либо еще. Неизменная часть этого ответа верна. Ваши компоненты должны реагировать на обновления ваших данных. Я обнаружил, что большую часть времени при использовании redux я могу полностью исключить состояние и использовать чистые компоненты, которые будут обновляться при изменении данных в их реквизитах. И эти данные передаются как реквизиты с помощью функции connect из пакета react-redux.

bpylearner
4 сентября 2017 в 18:29
0

Я хотел бы добавить практический пример использования, в котором я думаю, что forceUpdate в порядке, но, пожалуйста, поправьте меня, если я пропустил какие-либо параметры: у меня есть контейнер, который должен выполнить обновление самого себя (стиль перевода) после того, как все дочерние узлы были смонтированы и запрашивается их ширина (которая устанавливается с помощью таблицы стилей и вне моего контроля). В этом случае this.setState (this.state); не было бы идиоматикой, поскольку в состоянии ничего не изменилось, и мне нужно только повторно запустить render (). Пожалуйста, поправьте меня, если я ошибаюсь.

pizzarob
4 сентября 2017 в 20:45
0

@bpylearner Я думаю, что передача значений перевода в качестве реквизита или использование состояния компонентов для значений перевода было бы лучшим способом сделать это, но если вы переводите значение через css - я не понимаю, зачем вам нужно использовать forceUpdate

bpylearner
5 сентября 2017 в 10:36
0

@realseanp Это был вариант, который я рассматривал, но значение translate - это то, о чем я не хочу, чтобы остальная часть приложения беспокоилась. Вместо этого приложение передает элемент, который нужно сфокусировать, компонент выполняет рендеринг, опрашивает все его ссылки для вычисления правильных смещений, а затем принудительно выполняет повторный рендеринг для обновления до правильного положения. Дочерние размеры (до моих последних тестов) доступны только после того, как рендеринг уже выполнен. Поскольку состояние остальной части приложения не изменилось, я подумал, что forceUpdate было бы более идиоматично, если бы не использовать метод this.setState (...).

tonix
23 июля 2018 в 12:33
1

this.setState({ key: Math.random() }); Спасатель, спасибо за это!

Gaurav Ojha
15 января 2019 в 06:24
0

ИМО, это более подходящее решение. Спасибо!

VitalyB
3 апреля 2019 в 21:33
5

@atomkirk Я был того же мнения, что и вы, однако удивительно, что реагирую, документы одобряют и даже рекомендуют этот метод в некоторых случаях: reactjs.org/blog/2018/06/07/…

Mustkeem K
30 января 2020 в 13:18
0

math.random () создает проблему в сафари.

Peter Moore
30 апреля 2021 в 21:12
0

Я собирался проголосовать за изменение ключевого предложения - великолепно - пока не увидел редакционную статью. ОЧЕНЬ хорошая законная причина для принудительного обновления - это наличие анимации входа. Я уверен, что есть и другие.

pizzarob
1 мая 2021 в 04:00
1

@PeterMoore, да, еще одна причина может заключаться в том, что вы хотите повторно отрендерить компонент, тем самым обеспечивая запуск всех методов / хуков жизненного цикла из нового состояния.

sao
21 декабря 2021 в 21:34
0

да, просто убедитесь, что ссылочные объекты ваших ресурсов redux используют распространение для создания новой ссылки. redux / react не всегда замечает обновленный объект / массив с той же ссылкой. локальное состояние ваших компонентов должно быть в порядке, поскольку объекты состояния являются неизменяемыми (неизменяемыми, они создаются каждый раз новыми, реагируя с использованием setState в классе или хуков в функциональном состоянии), но если у вас есть состояние обновления саги или действия, создайте новую ссылку с расширением . это, вероятно, исправит некоторые проблемы OP, связанные с государством.

avatar
gcedo
4 июня 2015 в 11:51
40

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

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

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

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

AJFarkas
13 ноября 2015 в 01:18
2

Каков статус памяти состояния компонента? Если у меня пять компонентов на странице, и все они прослушивают одно хранилище, хранятся ли у меня данные пять раз в памяти или они являются ссылками? И разве все эти слушатели не складываются быстро? Почему лучше «просачиваться», чем просто передавать данные своей цели?

Stijn de Witt
31 января 2016 в 01:54
6

На самом деле рекомендации состоят в том, чтобы передавать данные хранилища в свойства компонента и использовать состояние компонента только для таких вещей, как состояние прокрутки и другие мелкие специфичные для пользовательского интерфейса вещи. Если вы используете redux (я рекомендую это), вы можете использовать функцию connect из react-redux для автоматического сопоставления состояния хранилища с реквизитами компонентов, когда это необходимо, на основе предоставленной вами функции сопоставления.

King Friday
28 мая 2017 в 14:53
1

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

AJP
3 июля 2017 в 16:55
5

"forceUpdate () всегда следует избегать" неверно. В документации четко сказано: «Обычно вам следует избегать любого использования forceUpdate ()». Существуют допустимые варианты использования forceUpdate

gcedo
17 ноября 2017 в 10:55
2

@AJP, ты абсолютно прав, это было личное предубеждение :) Я отредактировал ответ.