Eu li toneladas de documentação - em todos os sites populares - mas em nenhum lugar encontrei uma resposta. Estou perguntando aqui para ver se perdi alguma documentação ou uma parte vital de compreensão.
O problema que vejo é que a função mapStateToProps () do meu componente só é chamada uma vez (na inicialização). Nunca mais é chamado, mesmo quando o redutor é chamado. Estou começando a pensar que é porque não entendo como as ações / redutores / suportes estão todos interligados.
Deixe-me primeiro dizer o que eu _do_ sei:
Mas ... Eu não encontrei uma descrição de como as ações, redutores e mapStateToProps se relacionam entre si. Minhas perguntas:
Você examinou http://redux.js.org/docs/Trou trouble.html e verificou que seu caso não é um dos problemas que ele descreve?
Eu penso na loja como um objeto, com propriedades que armazenam dados sobre o estado do aplicativo. Essas propriedades de nível superior podem ser objetos que fornecem uma subárvore para conter informações sobre uma parte do aplicativo. Isso é verdade?
Sim. Store mantém o objeto de estado de nível superior internamente. Ele fornece acesso a esse objeto de store.getState()
. Esse objeto corresponde a tudo o que você retorna do redutor de raiz e geralmente é semelhante a uma árvore.
Quando uma ação é enviada para a loja, qual propriedade (ou subárvore da loja) é atualizada? Parece que há uma criação / associação automática a uma propriedade dentro da loja, mas como isso é determinado?
Não, não há criação automática de nada. A loja apenas começa a apontar para o resultado de chamar seu redutor de raiz.
Se o seu redutor de raiz é uma função que você mesmo escreveu, o resultado é o que você obterá após despachar uma ação:
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;
}
Se você _gerar_ o redutor de raiz com algo como combineReducers({ foo, bar })
, é o mesmo como se você escrevesse um redutor de raiz à mão parecido com este:
function root(state = {}, action) {
return {
foo: foo(state.foo, action),
bar: bar(state.bar, action)
}
}
É daí que muitas vezes vêm as propriedades no estado. Seus valores são determinados pelo que seus redutores foo
e bar
retornaram durante o manuseio da ação, respectivamente, portanto, novamente, você tem controle total sobre eles.
Como mapStateToProps "sabe" se deve ouvir as atualizações da propriedade / parte adequada da loja? O que os une?
Você passa mapStateToProps
para a função connect()
. Ele gera um componente que usa store.subscribe()
para ouvir todas as mudanças na loja. Quando a loja muda, o componente gerado lê store.getState()
e passa para mapStateToProps()
para determinar os próximos adereços a serem passados. Se eles mudaram, ele irá renderizar novamente seu componente com eles.
Como posso depurar isso? O que eu preciso fazer para descobrir por que o envio da ação não parece disparar mapStateToProps?
Adicione um middleware como https://github.com/theaqua/redux-logger. Verifique se o estado é atualizado após a ação da maneira esperada. Se mapStateToProps
nem mesmo for chamado, isso significa que
dispatch()
uma ação e acabou de chamar um criador de ação,connect()
não se preocupou em chamar mapStateToProps()
.Ambos os casos são descritos em http://redux.js.org/docs/Trou troubleshooting.html.
No futuro, pedimos que você tente fazer perguntas no StackOverflow primeiro e usar o rastreador de problemas quando tiver certeza de que há um bug na biblioteca que você consegue reproduzir de forma consistente com um exemplo que pode compartilhar.
Obrigado!
Respondendo em ordem:
{ users : {}, posts : {}, comments : {}, ui : {} }
. Consulte http://redux.js.org/docs/FAQ.html#reducers -share-state e http://redux.js.org/docs/FAQ.html#organizing -state-nested-data.combineReducers
que vem com o Redux torna mais fácil ter uma função redutora específica que é responsável por gerenciar atualizações para uma determinada fatia de dados, como: const combinedReducer = combineReducers({users : userReducer, posts : postReducer, comments : commentsReducer, ui : uiReducer});
connect()
inscreve e então chama mapStateToProps
do componente para ver se algum dos dados extraídos mudou desde a última vez. Não há assinatura de estado aninhada específica. Consulte http://redux.js.org/docs/FAQ.html#store -setup-subscriptions.Espero que isso ajude a esclarecer as coisas.
Além disso, como disse Dan: questões de uso geralmente são mais adequadas para Stack Overflow. Além disso, a comunidade Reactiflux no Discord tem vários canais de bate-papo dedicados a tecnologias relacionadas ao React, incluindo Redux, e sempre há várias pessoas por aí dispostas a responder perguntas. Existe um link de convite em https://www.reactiflux.com .
@ richb-hanover Você provavelmente testemunhou a comparação superficial que é realizada pela conexão react-redux. Isso significa que objetos aninhados em outros objetos não resultarão em renderização novamente, mesmo se forem modificados, porque apenas a chave raiz é verificada para modificações.
http://redux.js.org/docs/faq/ReactRedux.html#why -isnt-my-component-re-rendering-or-my-mapstatetoprops-running
@ richb-hanover No meu caso, @gaearon detectou o problema:
Ou seu redutor retornou um valor referencialmente idêntico, portanto, connect () não se incomodou em chamar mapStateToProps ().
Eu estava usando essa correção e esse foi o código resultante
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);
Como eu não passei o parâmetro id
para a chamada de incremento (seja como increment(id)
no modelo, ou por meio do segundo parâmetro em mapDispatchToProps
), a loja não atualize corretamente, portanto, verifique se esse é o seu problema.
Comentários muito úteis
Você examinou http://redux.js.org/docs/Trou trouble.html e verificou que seu caso não é um dos problemas que ele descreve?
Sim. Store mantém o objeto de estado de nível superior internamente. Ele fornece acesso a esse objeto de
store.getState()
. Esse objeto corresponde a tudo o que você retorna do redutor de raiz e geralmente é semelhante a uma árvore.Não, não há criação automática de nada. A loja apenas começa a apontar para o resultado de chamar seu redutor de raiz.
Se o seu redutor de raiz é uma função que você mesmo escreveu, o resultado é o que você obterá após despachar uma ação:
Se você _gerar_ o redutor de raiz com algo como
combineReducers({ foo, bar })
, é o mesmo como se você escrevesse um redutor de raiz à mão parecido com este:É daí que muitas vezes vêm as propriedades no estado. Seus valores são determinados pelo que seus redutores
foo
ebar
retornaram durante o manuseio da ação, respectivamente, portanto, novamente, você tem controle total sobre eles.Você passa
mapStateToProps
para a funçãoconnect()
. Ele gera um componente que usastore.subscribe()
para ouvir todas as mudanças na loja. Quando a loja muda, o componente gerado lêstore.getState()
e passa paramapStateToProps()
para determinar os próximos adereços a serem passados. Se eles mudaram, ele irá renderizar novamente seu componente com eles.Adicione um middleware como https://github.com/theaqua/redux-logger. Verifique se o estado é atualizado após a ação da maneira esperada. Se
mapStateToProps
nem mesmo for chamado, isso significa quedispatch()
uma ação e acabou de chamar um criador de ação,connect()
não se preocupou em chamarmapStateToProps()
.Ambos os casos são descritos em http://redux.js.org/docs/Trou troubleshooting.html.
No futuro, pedimos que você tente fazer perguntas no StackOverflow primeiro e usar o rastreador de problemas quando tiver certeza de que há um bug na biblioteca que você consegue reproduzir de forma consistente com um exemplo que pode compartilhar.
Obrigado!