Redux: mapStateToProps () не вызывается

Созданный на 13 мая 2016  ·  4Комментарии  ·  Источник: reduxjs/redux

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

Проблема, которую я вижу, заключается в том, что функция моего компонента mapStateToProps () вызывается только один раз (при инициализации). Он никогда не вызывается снова, даже когда вызывается редуктор. Я начинаю думать, что это потому, что я не понимаю, как все действия / редукторы / реквизиты связаны вместе.

Позвольте мне сначала сказать то, что я знаю:

  • Я понимаю, что действия описывают обновления части (свойства) этого магазина.
  • Я понимаю, что каждый редуктор отвечает за обновление своего фрагмента / части магазина. (И я определенно понимаю, что редуктор должен возвращать новый объект для своего среза.)
  • Я понимаю, что mapStateToProps «забирает» определенные данные из хранилища и передает их в качестве свойств названному компоненту.

Но ... Я не нашел описания того, как действия, редукторы и mapStateToProps связаны друг с другом. Мои вопросы:

  • Я думаю о хранилище как об объекте, свойства которого содержат данные о состоянии приложения. Эти свойства верхнего уровня могут быть объектами, которые предоставляют поддерево для хранения информации о части приложения. Это правда?
  • Когда действие отправляется в магазин, какое свойство (или поддерево магазина) обновляется? Кажется, что в магазине происходит автоматическое создание / привязка к свойству, но как это определяется?
  • Как mapStateToProps «знает», что нужно прислушиваться к обновлениям соответствующего свойства / части магазина? Что их связывает?
  • Как я мог это отладить? Что мне нужно сделать, чтобы выяснить, почему отправка действия не вызывает срабатывания mapStateToProps?
  • Или я все неправильно думаю ...?
question

Самый полезный комментарий

Вы прошли http://redux.js.org/docs/Troubleshooting.html и убедились, что ваш случай не является одной из описываемых в нем проблем?

Я думаю о хранилище как об объекте, свойства которого содержат данные о состоянии приложения. Эти свойства верхнего уровня могут быть объектами, которые предоставляют поддерево для хранения информации о части приложения. Это правда?

Ага. Store хранит внутри себя объект состояния верхнего уровня. Он обеспечивает доступ к этому объекту из store.getState() . Этот объект соответствует тому, что вы возвращаете из корневого редуктора, и часто он похож на дерево.

Когда действие отправляется в магазин, какое свойство (или поддерево магазина) обновляется? Кажется, что в магазине происходит автоматическое создание / привязка к свойству, но как это определяется?

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

Если корневой редюсер - это функция, которую вы написали сами, ее результат - это то, что вы получите после отправки действия:

function counter(state = 0, action) {
  if (action.type === 'INCREMENT') {
    return state + 1; // will become the next value of store.getState() after store.dispatch()
  }
  return state;
}

Если вы _генерируете_ корневой редуктор с чем-то вроде combineReducers({ foo, bar }) , это будет то же самое, как если бы вы вручную написали корневой редуктор, который выглядит так:

function root(state = {}, action) {
  return {
    foo: foo(state.foo, action),
    bar: bar(state.bar, action)
  }
}

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

Как mapStateToProps «знает», что нужно прислушиваться к обновлениям соответствующего свойства / части магазина? Что их связывает?

Вы передаете mapStateToProps connect() . Он генерирует компонент, который использует store.subscribe() для прослушивания каждого изменения в хранилище. Когда хранилище изменилось, сгенерированный компонент считывает store.getState() и передает его в mapStateToProps() чтобы определить следующие свойства, которые нужно передать. Если они изменились, он повторно отобразит ваш компонент вместе с ними.

Как я мог это отладить? Что мне нужно сделать, чтобы выяснить, почему отправка действия не вызывает срабатывания mapStateToProps?

Добавьте промежуточное ПО, например https://github.com/theaqua/redux-logger. Убедитесь, что состояние обновляется после действия так, как вы ожидаете. Если mapStateToProps даже не вызывается, это означает, что

  • Либо вы забыли dispatch() действие и только что вызвали создателя действия,
  • Или ваш редуктор вернул ссылочно идентичное значение, поэтому connect() не стал вызывать mapStateToProps() .

Оба этих случая описаны в http://redux.js.org/docs/Troubleshooting.html.

В будущем мы просим вас сначала попытаться задать вопросы о StackOverflow и использовать средство отслеживания проблем, когда вы уверены, что в библиотеке есть ошибка, которую вы можете постоянно воспроизводить с помощью примера, которым вы можете поделиться.

Благодаря!

Все 4 Комментарий

Вы прошли http://redux.js.org/docs/Troubleshooting.html и убедились, что ваш случай не является одной из описываемых в нем проблем?

Я думаю о хранилище как об объекте, свойства которого содержат данные о состоянии приложения. Эти свойства верхнего уровня могут быть объектами, которые предоставляют поддерево для хранения информации о части приложения. Это правда?

Ага. Store хранит внутри себя объект состояния верхнего уровня. Он обеспечивает доступ к этому объекту из store.getState() . Этот объект соответствует тому, что вы возвращаете из корневого редуктора, и часто он похож на дерево.

Когда действие отправляется в магазин, какое свойство (или поддерево магазина) обновляется? Кажется, что в магазине происходит автоматическое создание / привязка к свойству, но как это определяется?

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

Если корневой редюсер - это функция, которую вы написали сами, ее результат - это то, что вы получите после отправки действия:

function counter(state = 0, action) {
  if (action.type === 'INCREMENT') {
    return state + 1; // will become the next value of store.getState() after store.dispatch()
  }
  return state;
}

Если вы _генерируете_ корневой редуктор с чем-то вроде combineReducers({ foo, bar }) , это будет то же самое, как если бы вы вручную написали корневой редуктор, который выглядит так:

function root(state = {}, action) {
  return {
    foo: foo(state.foo, action),
    bar: bar(state.bar, action)
  }
}

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

Как mapStateToProps «знает», что нужно прислушиваться к обновлениям соответствующего свойства / части магазина? Что их связывает?

Вы передаете mapStateToProps connect() . Он генерирует компонент, который использует store.subscribe() для прослушивания каждого изменения в хранилище. Когда хранилище изменилось, сгенерированный компонент считывает store.getState() и передает его в mapStateToProps() чтобы определить следующие свойства, которые нужно передать. Если они изменились, он повторно отобразит ваш компонент вместе с ними.

Как я мог это отладить? Что мне нужно сделать, чтобы выяснить, почему отправка действия не вызывает срабатывания mapStateToProps?

Добавьте промежуточное ПО, например https://github.com/theaqua/redux-logger. Убедитесь, что состояние обновляется после действия так, как вы ожидаете. Если mapStateToProps даже не вызывается, это означает, что

  • Либо вы забыли dispatch() действие и только что вызвали создателя действия,
  • Или ваш редуктор вернул ссылочно идентичное значение, поэтому connect() не стал вызывать mapStateToProps() .

Оба этих случая описаны в http://redux.js.org/docs/Troubleshooting.html.

В будущем мы просим вас сначала попытаться задать вопросы о StackOverflow и использовать средство отслеживания проблем, когда вы уверены, что в библиотеке есть ошибка, которую вы можете постоянно воспроизводить с помощью примера, которым вы можете поделиться.

Благодаря!

Отвечая по порядку:

  • Redux обычно предполагает, что верхняя часть вашего дерева состояний представляет собой простой объект Javascript (хотя также часто используются типы данных, предоставляемые библиотекой Immutable.js). Что вы помещаете внутрь этого объекта и как вы структурируете его содержимое, полностью зависит от вас. Самый распространенный подход - организовать вещи по доменам. Например, дерево состояний для гипотетического приложения блога может выглядеть так: { users : {}, posts : {}, comments : {}, ui : {} } . См http://redux.js.org/docs/FAQ.html#reducers -Share-состояние и http://redux.js.org/docs/FAQ.html#organizing -state-гнездовые данные.
  • С технической точки зрения, когда вы создаете свой магазин, вы определяете _single_ функцию "reducer". Эта функция вызывается с текущим состоянием и входящим действием и отвечает за возврат нового объекта состояния с _все_ соответствующими обновлениями, выполняемыми неизменным образом (т. Е. Без прямого редактирования на месте - все, что нужно обновить, должно быть выполняется путем создания копий, их изменения и возврата вместо них копий, а обновление вложенного поля означает также копирование и обновление всех его предков). Однако, поскольку размещение каждого последнего бита логики обновления в одной гигантской функции - плохая идея, эта логика обновления почти всегда разбивается на более мелкие повторно используемые вспомогательные функции редуктора. Как вы структурируете эту логику, снова зависит от вас, но ничего не обновляется автоматически - это зависит от вашей логики редуктора. Утилита combineReducers , поставляемая с Redux, упрощает создание конкретной функции редуктора, отвечающей за управление обновлениями определенного фрагмента данных, например: const combinedReducer = combineReducers({users : userReducer, posts : postReducer, comments : commentsReducer, ui : uiReducer});
  • Redux просто уведомляет всех подписчиков всякий раз, когда в хранилище выполняется _любое_ действие. Функция React Redux connect() подписывается, а затем вызывает mapStateToProps компонента, чтобы узнать, изменились ли какие-либо из извлеченных данных с последнего раза. Нет специальной подписки с вложенным состоянием. См http://redux.js.org/docs/FAQ.html#store -SETUP-подписки.
  • Причина номер один того, что компонент не обновляется, - это прямая мутация состояния в вашем редукторе. См http://redux.js.org/docs/FAQ.html#react -на-перерисовка. Помимо этого, существует ряд надстроек, которые вы можете использовать для отладки и регистрации действий. Многие из них перечислены на https://github.com/markerikson/redux-ecosystem-links/blob/master/devtools.md.

Надеюсь, это поможет прояснить ситуацию.

Кроме того, как сказал Дэн: вопросы об использовании обычно лучше подходят для Stack Overflow. Кроме того, у сообщества Reactiflux на Discord есть куча каналов чата, посвященных технологиям, связанным с React, включая Redux, и всегда есть много людей, готовых ответить на вопросы. Ссылка для приглашения находится на https://www.reactiflux.com .

@ richb-hanover Вы, наверное, были свидетелями неглубокого сравнения, которое выполняется соединением react-redux. Это означает, что объекты, вложенные в другие объекты, не приведут к повторной визуализации, даже если они были изменены, потому что только корневой ключ проверяется на наличие изменений.

http://redux.js.org/docs/faq/ReactRedux.html#why -isnt-my-component-re-rendering-or-my-mapstatetoprops-running

@ richb-hanover В моем случае @gaearon обнаружил проблему:

Или ваш редуктор вернул ссылочно идентичное значение, поэтому connect () не стал вызывать mapStateToProps ().

Я использовал это исправление, и получился код

const mapStateToProps = (state, props) => {
    return { id: props.id, currentState: state[props.id] };
}

const mapDispatchToProps = (dispatch) => {
    return {
        increment: (id) => {
            dispatch({ type: 'INCREMENT', id: id })
        }
    }
}

const DumbListItem = ({
    increment,
    id,
    currentState
}) => (
        <div>
            <li onClick={() => increment()}>{id}</li>
            <span>{currentState}</span>
        </div>
    );

export const ConnectedListItem = connect(
    mapStateToProps,
    mapDispatchToProps
)(DumbListItem);

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

Была ли эта страница полезной?
0 / 5 - 0 рейтинги