Повторно відтворити компонент React при зміні властивості


90

Я намагаюся відокремити презентаційний компонент від компонента контейнера. У мене є а SitesTableі а SitesTableContainer. Контейнер відповідає за активацію дій відновлення для отримання відповідних сайтів на основі поточного користувача.

Проблема полягає в тому, що поточний користувач отримується асинхронно після того, як компонент контейнера відтворюється спочатку. Це означає, що компонент контейнера не знає, що йому потрібно повторно виконати код у своїй componentDidMountфункції, яка буде оновлювати дані для надсилання до SitesTable. Я думаю, мені потрібно повторно відтворити компонент контейнера, коли зміниться один із його реквізитів (користувач). Як мені це зробити правильно?

class SitesTableContainer extends React.Component {
    static get propTypes() {
      return {
        sites: React.PropTypes.object,
        user: React.PropTypes.object,
        isManager: React.PropTypes.boolean
      }
     }

    componentDidMount() {
      if (this.props.isManager) {
        this.props.dispatch(actions.fetchAllSites())
      } else {
        const currentUserId = this.props.user.get('id')
        this.props.dispatch(actions.fetchUsersSites(currentUserId))
      }  
    }

    render() {
      return <SitesTable sites={this.props.sites}/>
    }
}

function mapStateToProps(state) {
  const user = userUtils.getCurrentUser(state)

  return {
    sites: state.get('sites'),
    user,
    isManager: userUtils.isManager(user)
  }
}

export default connect(mapStateToProps)(SitesTableContainer);

у вас є доступні деякі інші функції, такі як componentDidUpdate або, можливо, та, яку ви шукаєте, componentWillReceiveProps (nextProps), якщо ви хочете щось запустити, коли змінюється реквізит
thsorens

Чому вам потрібно повторно відтворювати SitesTable, якщо він не змінює свій реквізит?
QoP

@QoP дії, що надсилаються componentDidMount, змінять sitesвузол у стані програми, який передається в SitesTable. Вузол SitesStable sitesбуде змінюватися.
Девід

О, розумію, я напишу відповідь.
QoP

1
Як досягти цього за допомогою функціональної складової
yaswanthkoneri

Відповіді:


115

Ви повинні додати умову у своєму componentDidUpdateметоді.

Приклад використовується fast-deep-equalдля порівняння об’єктів.

import equal from 'fast-deep-equal'

...

constructor(){
  this.updateUser = this.updateUser.bind(this);
}  

componentDidMount() {
  this.updateUser();
}

componentDidUpdate(prevProps) {
  if(!equal(this.props.user, prevProps.user)) // Check if it's a new user, you can also use some unique property, like the ID  (this.props.user.id !== prevProps.user.id)
  {
    this.updateUser();
  }
} 

updateUser() {
  if (this.props.isManager) {
    this.props.dispatch(actions.fetchAllSites())
  } else {
    const currentUserId = this.props.user.get('id')
    this.props.dispatch(actions.fetchUsersSites(currentUserId))
  }  
}

Використання хуків (React 16.8.0+)

import React, { useEffect } from 'react';

const SitesTableContainer = ({
  user,
  isManager,
  dispatch,
  sites,
}) => {
  useEffect(() => {
    if(isManager) {
      dispatch(actions.fetchAllSites())
    } else {
      const currentUserId = user.get('id')
      dispatch(actions.fetchUsersSites(currentUserId))
    }
  }, [user]); 

  return (
    return <SitesTable sites={sites}/>
  )

}

Якщо опис, який ви порівнюєте, є об'єктом або масивом, вам слід використовувати useDeepCompareEffectзамість useEffect.


Зверніть увагу, що JSON.stringify може використовуватися лише для такого порівняння, якщо він стабільний (за специфікацією це не так), тому він видає однакові результати для тих самих входів. Я рекомендую порівнювати властивості ідентифікаторів об'єктів користувача або передавати userId-і в реквізити та порівнювати їх, щоб уникнути зайвих перезавантажень.
László Kardinál

4
Зверніть увагу, що метод життєвого циклу компонентаWillReceiveProps застарілий і, ймовірно, буде вилучений у React 17. Використання комбінації componentDidUpdate та нового методу getDerivedStateFromProps є запропонованою стратегією від команди розробників React. Більше у своєму дописі в блозі: responsejs.org/blog/2018/03/27/update-on-async-rendering.html
michaelpoltorak

@QoP Другий приклад, з React Hooks, буде демонтувати та перемонтувати при будь-яких userзмінах? Наскільки це дорого?
Роботрон

30

ComponentWillReceiveProps()буде припинено в майбутньому через помилки та невідповідності. Альтернативним рішенням для повторного відтворення компонента при зміні реквізитів є використання ComponentDidUpdate()та ShouldComponentUpdate().

ComponentDidUpdate()викликається щоразу, коли компонент оновлюється І якщо ShouldComponentUpdate()повертає true (Якщо ShouldComponentUpdate()не визначено, він повертається trueза замовчуванням).

shouldComponentUpdate(nextProps){
    return nextProps.changedProp !== this.state.changedProp;
}

componentDidUpdate(props){
    // Desired operations: ex setting state
}

Цю саму поведінку можна здійснити, використовуючи лише ComponentDidUpdate()метод, включивши в нього умовний оператор.

componentDidUpdate(prevProps){
    if(prevProps.changedProp !== this.props.changedProp){
        this.setState({          
            changedProp: this.props.changedProp
        });
    }
}

Якщо хтось намагається встановити стан без умовного чи без визначення ShouldComponentUpdate()компонента, він буде нескінченно повторно відтворюватися


2
Цю відповідь потрібно проголосувати (принаймні поки що), оскільки componentWillReceivePropsвона майже не підтримується, і її не рекомендують використовувати.
AnBisw

Друга форма (умовне твердження всередині componentDidUpdate) працює для мене, оскільки я хочу, щоб інші зміни стану все-таки відбувалися, наприклад, закриття флеш-повідомлення.
Маленький Мозок,

11

Ви можете використовувати KEYунікальний ключ (комбінація даних), який змінюється за допомогою реквізиту, і цей компонент буде повторно відтворений з оновленим реквізитом.


4
componentWillReceiveProps(nextProps) { // your code here}

Я думаю, що це подія, яка вам потрібна. componentWillReceivePropsспрацьовує щоразу, коли ваш компонент отримує щось через реквізит. Звідти ви можете пройти перевірку, а потім робити все, що хочете.


11
componentWillReceivePropsзастарілий *
Майхан Ніджат,

2

Я порекомендував би поглянути на цю мою відповідь і перевірити, чи вона відповідає вашому витворенню. Якщо я розумію вашу справжню проблему, це полягає в тому, що ви просто неправильно використовуєте свою дію асинхронізації та оновлюєте redux "store", який автоматично оновить ваш компонент за допомогою нових реквізитів.

Цей розділ вашого коду:

componentDidMount() {
      if (this.props.isManager) {
        this.props.dispatch(actions.fetchAllSites())
      } else {
        const currentUserId = this.props.user.get('id')
        this.props.dispatch(actions.fetchUsersSites(currentUserId))
      }  
    }

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

Погляньте на цей приклад із redux-thunk :

function makeASandwichWithSecretSauce(forPerson) {

  // Invert control!
  // Return a function that accepts `dispatch` so we can dispatch later.
  // Thunk middleware knows how to turn thunk async actions into actions.

  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}

Вам не обов’язково використовувати redux-thunk, але це допоможе вам міркувати про подібні сценарії та писати код для відповідності.


правильно, я розумію це. Але куди саме ви відправляєте makeASandwichWithSecretSauce ваш компонент?
Девід

Я зв’яжу вас із репозиторієм із відповідним прикладом, чи використовуєте ви з вашим додатком маршрутизатор реакцій?
TameBadger

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

0

Зручним методом є наступний, після оновлення пропсу він автоматично відтворюватиме компонент:

render {

let textWhenComponentUpdate = this.props.text 

return (
<View>
  <Text>{textWhenComponentUpdate}</Text>
</View>
)

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.