Як компонент, підключений до redux, знає, коли повторно відтворювати?


86

Напевно, мені не вистачає чогось дуже очевидного, і я хотів би це очистити.

Ось моє розуміння.
У компоненті наївної реакції ми маємо states& props. Оновлення stateз setStateповторною візуалізацією всього компонента. propsв основному лише для читання, і їх оновлення не має сенсу.

У реакційному компоненті, який передплачує сховище редукцій, через щось подібне store.subscribe(render), він, очевидно, повторно рендериться при кожному оновленні магазину.

реактор-редукс має помічник, connect()який вводить частину дерева стану (що цікавить компонент) і actionCreators propsщодо компонента, як правило, за допомогою чогось типу

const TodoListComponent = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

Але з розумінням того, що a setStateмає важливе значення для того, TodoListComponentщоб реагувати на зменшення дерева дерев станів (повторне відображення), я не можу знайти жодного stateабо setStateпов'язаного з ним коду у TodoListфайлі компонента. Це читається приблизно так:

const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

Хтось може вказати мені в правильному напрямку щодо того, чого мені не вистачає?

PS Я слідую прикладу списку завдань, що входить до складу пакета redux .

Відповіді:


109

connectФункція генерує компонент оболонки, приєднується до магазину. Коли дія відправляється, повідомляється про зворотний виклик компонента обгортки. Потім вона запускає вашу mapStateфункцію, і поверхнево порівнює об’єкт результату з цього часу з об’єктом результату минулого разу (тому, якщо ви переписуєте поле сховища редукцій з тим самим значенням, це не спричинить повторне відображення). Якщо результати відрізняються, тоді він передає результати до вашого "справжнього" компонента як реквізит.

Ден Абрамов написав чудову спрощену версію connectat ( connect.js ), яка ілюструє основну ідею, хоча і не відображає жодної роботи з оптимізації. У мене також є посилання на ряд статей про виступ Redux, в яких обговорюються деякі суміжні ідеї.

оновлення

React-Redux v6.0.0 вніс деякі основні внутрішні зміни в те, як підключені компоненти отримують дані з магазину.

Як частина цього, я написав допис, який пояснює, як працюють connectAPI та його внутрішні елементи, і як вони змінювались з часом:

Ідіоматичний Redux: Історія та впровадження React-Redux


Дякую! Ви та сама людина, яка допомогла мені днями @ HN - news.ycombinator.com/item?id=12307621 корисними посиланнями? Приємно познайомитися :)
Джо Льюїс

4
Так, це я :) Я витрачаю шлях надто багато часу , шукаючи для обговорення Redux онлайн - Reddit, HN, SO, Medium, а також різні інші місця. Радий допомогти! (Також FYI, я настійно рекомендую канали чату Reactiflux на Discord. Багато людей тусуються там і відповідають на запитання. Зазвичай я там в Інтернеті ввечері за американським стандартним часом. Запрошення на посилання знаходиться на reactiflux.com .)
markerikson,

Якщо припустити, що це дало відповідь на запитання, ви приймаєте відповідь? :)
markerikson

Це порівняння самих об’єктів чи властивостей тих, що передують / після об’єктів? Якщо це останнє, чи перевіряє він лише перший рівень, чи це ви маєте на увазі під словом "дрібний?"
Енді

Так, засіб «дрібної перевірки рівності» , щоб порівняти перші поля на рівні кожного об'єкта: prev.a === current.a && prev.b === current.b && ...... Це передбачає, що будь-які зміни даних призведуть до нових посилань, а це означає, що замість прямої мутації потрібні незмінні оновлення даних.
markerikson

13

Моя відповідь - трохи поза лівим полем. Це проливає світло на проблему, яка привела мене до цієї посади. У моєму випадку здавалося, що додаток не рендерінг, хоча він отримав новий реквізит.
Розробники React отримали відповідь на це часто задаване запитання, яка нагадує, що якщо (магазин) був мутований, 99% випадків, коли причина реакції не буде повторно відображатися. Проте нічого про інші 1%. Мутація тут не була.


TLDR;

componentWillReceivePropsце те, як stateможна синхронізувати нове props.

Прикордонний випадок: Після stateпоновлення, то додаток робить повторно винести!


Виявляється, якщо ваш додаток використовує лише stateдля відображення своїх елементів, він propsможе оновлюватись, але stateне буде, тому жоден повторний візуалізація не буде.

У мене stateце було залежно від propsотриманого від redux store. Мені потрібні дані ще не були в магазині, тому я їх отримав componentDidMount, як слід. Я отримав реквізит назад, коли мій редуктор оновив магазин, оскільки мій компонент підключений через mapStateToProps. Але сторінка не відображалася, і штат все ще був порожній рядок.

Прикладом цього є скажімо, що користувач завантажив сторінку "редагувати допис" із збереженої URL-адреси. Ви маєте доступ до postIdURL-адреси з URL-адреси, але інформації storeще немає, тому ви отримуєте її. Елементи на вашій сторінці є контрольованими компонентами, тож усі дані, які ви відображаєте, знаходяться state.

За допомогою redux дані було отримано, магазин оновлено та компонент connectвиправлено, але програма не відображала змін. Якщо придивитися уважніше, propsбули отримані, але програма не оновилася. stateне оновлено.

Ну, propsбуде оновлювати та поширювати, але stateне буде. Вам потрібно спеціально сказати, stateщоб оновити.

Ви не можете цього зробити render(), і componentDidMountвже закінчили цикли.

componentWillReceivePropsтут ви оновлюєте stateвластивості, які залежать від зміненого propзначення.

Приклад використання:

componentWillReceiveProps(nextProps){
  if (this.props.post.category !== nextProps.post.category){
    this.setState({
      title: nextProps.post.title,
      body: nextProps.post.body,
      category: nextProps.post.category,
    })
  }      
}

Я повинен вигукнути цю статтю, яка просвітлила мене щодо рішення, про яке не згадували десятки інших дописів, блогів та репозиторіїв. Будь-хто, хто мав проблеми з пошуком відповіді на цю очевидно неясну проблему, ось вона:

Методи життєвого циклу компонентів ReactJs - глибоке занурення

componentWillReceivePropsтут ви будете оновлювати, stateщоб синхронізуватися з propsоновленнями.

Після stateпоновлення, а потім поля в залежності від state справ повторної візуалізації!


3
З React 16.3року, ви повинні використовувати getDerivedStateFromPropsзамість componentWillReceiveProps. Але насправді, ви навіть не повинні робити все те, як сформульовано тут
kyw

це не працює, коли стан змінюється, тоді componentWillReceiveProps спрацює так багато випадків, коли я намагаюся, це не працює. Особливо, багаторівневий вкладений або складний реквізит, тому мені доводиться призначати компонент із ідентифікатором та запускати зміну для нього та оновлювати стан для повторної рендерингу нових даних
Травень Погода VN

0

Ця відповідь - короткий виклад статті Брайана Вона під назвою " Вам, мабуть, не потрібна похідна держава" (07 червня 2018 р.).

Виведення стану з реквізиту є антивізером у всіх його формах. В тому числі з використанням старшого componentWillReceivePropsта нового getDerivedStateFromProps.

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

Дві рекомендації щодо найкращих практик

Рекомендація 1. Повністю контрольований компонент
function EmailInput(props) {
  return <input onChange={props.onChange} value={props.email} />;
}
Рекомендація 2. Повністю неконтрольований компонент із ключем
// parent class
class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
}

// child instance
<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

Дві альтернативи, якщо з якихось причин рекомендації не підходять для вашої ситуації.

Альтернатива 1: Скиньте неконтрольований компонент за допомогою ідентифікатора
class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail,
    prevPropsUserID: this.props.userID
  };

  static getDerivedStateFromProps(props, state) {
    // Any time the current user changes,
    // Reset any parts of state that are tied to that user.
    // In this simple example, that's just the email.
    if (props.userID !== state.prevPropsUserID) {
      return {
        prevPropsUserID: props.userID,
        email: props.defaultEmail
      };
    }
    return null;
  }

  // ...
}
Варіант 2: Скиньте неконтрольований компонент методом екземпляра
class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail
  };

  resetEmailForNewUser(newEmail) {
    this.setState({ email: newEmail });
  }

  // ...
}

-2

як я знаю лише те, що робить redux, при зміні стану магазину викликається componentWillRecieveProps, якщо ваш компонент був залежним від мутованого стану, і тоді вам слід змусити ваш компонент оновити його, як це

1-магазин зміни стану-2-дзвінка (componentWillRecieveProps (() => {3-компонентна зміна стану}))

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