Я прочитал тонны документации - на всех популярных сайтах - но нигде не нашел ответа. Я прошу здесь увидеть, не упустил ли я какую-то документацию или важный элемент понимания.
Проблема, которую я вижу, заключается в том, что функция моего компонента mapStateToProps () вызывается только один раз (при инициализации). Он никогда не вызывается снова, даже когда вызывается редуктор. Я начинаю думать, что это потому, что я не понимаю, как все действия / редукторы / реквизиты связаны вместе.
Позвольте мне сначала сказать то, что я знаю:
Но ... Я не нашел описания того, как действия, редукторы и mapStateToProps связаны друг с другом. Мои вопросы:
Вы прошли 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 и использовать средство отслеживания проблем, когда вы уверены, что в библиотеке есть ошибка, которую вы можете постоянно воспроизводить с помощью примера, которым вы можете поделиться.
Благодаря!
Отвечая по порядку:
{ users : {}, posts : {}, comments : {}, ui : {} }
. См http://redux.js.org/docs/FAQ.html#reducers -Share-состояние и http://redux.js.org/docs/FAQ.html#organizing -state-гнездовые данные.combineReducers
, поставляемая с Redux, упрощает создание конкретной функции редуктора, отвечающей за управление обновлениями определенного фрагмента данных, например: const combinedReducer = combineReducers({users : userReducer, posts : postReducer, comments : commentsReducer, ui : uiReducer});
connect()
подписывается, а затем вызывает mapStateToProps
компонента, чтобы узнать, изменились ли какие-либо из извлеченных данных с последнего раза. Нет специальной подписки с вложенным состоянием. См http://redux.js.org/docs/FAQ.html#store -SETUP-подписки.Надеюсь, это поможет прояснить ситуацию.
Кроме того, как сказал Дэн: вопросы об использовании обычно лучше подходят для 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
), магазин не обновите правильно, поэтому проверьте, возможно, это ваша проблема.
Самый полезный комментарий
Вы прошли http://redux.js.org/docs/Troubleshooting.html и убедились, что ваш случай не является одной из описываемых в нем проблем?
Ага. Store хранит внутри себя объект состояния верхнего уровня. Он обеспечивает доступ к этому объекту из
store.getState()
. Этот объект соответствует тому, что вы возвращаете из корневого редуктора, и часто он похож на дерево.Нет, автоматического создания чего-либо нет. Магазин просто начинает указывать на результат вызова корневого редуктора.
Если корневой редюсер - это функция, которую вы написали сами, ее результат - это то, что вы получите после отправки действия:
Если вы _генерируете_ корневой редуктор с чем-то вроде
combineReducers({ foo, bar })
, это будет то же самое, как если бы вы вручную написали корневой редуктор, который выглядит так:Вот откуда часто берутся свойства в штате. Их значения определяются тем, что ваши редукторы
foo
иbar
вернули при обработке действия, соответственно, так что вы снова полностью контролируете их.Вы передаете
mapStateToProps
connect()
. Он генерирует компонент, который используетstore.subscribe()
для прослушивания каждого изменения в хранилище. Когда хранилище изменилось, сгенерированный компонент считываетstore.getState()
и передает его вmapStateToProps()
чтобы определить следующие свойства, которые нужно передать. Если они изменились, он повторно отобразит ваш компонент вместе с ними.Добавьте промежуточное ПО, например https://github.com/theaqua/redux-logger. Убедитесь, что состояние обновляется после действия так, как вы ожидаете. Если
mapStateToProps
даже не вызывается, это означает, чтоdispatch()
действие и только что вызвали создателя действия,connect()
не стал вызыватьmapStateToProps()
.Оба этих случая описаны в http://redux.js.org/docs/Troubleshooting.html.
В будущем мы просим вас сначала попытаться задать вопросы о StackOverflow и использовать средство отслеживания проблем, когда вы уверены, что в библиотеке есть ошибка, которую вы можете постоянно воспроизводить с помощью примера, которым вы можете поделиться.
Благодаря!