Redux: Como chamar ações sem usar @connect?

Criado em 30 jul. 2015  ·  32Comentários  ·  Fonte: reduxjs/redux

Eu tenho um contêiner de cabeçalho que passa dados de imagem (url, tamanho) para um componente de cabeçalho usando

@connect(state => ({
  header: state.header
}))
export default class HeaderContainer extends Component {
 /// pass props.header data to child Header which renders image.
}

Então eu tenho outro componente que precisa chamar as ações do cabeçalho para atualizar o estado do cabeçalho. Não usa o estado, apenas dispara ações. Afaik, preciso fazer algo assim:

const { dispatch } = this.props
const actions = bindActionCreators(headerActions, dispatch)

O problema é que this.props.dispatch só parece estar disponível quando você usa o decorador @connect . O segundo componente não está interessado no estado do cabeçalho. Eu realmente preciso usar o conectar aqui ou pode de alguma forma vincular as ações de uma maneira diferente? Parece errado usar @connect se eu não usar os dados.

question

Comentários muito úteis

Você pode passar dispatch para aquele componente manualmente como um suporte.

Todos 32 comentários

Não tenho certeza se é um caminho "certo" ou não, mas você pode acessar a loja pelo contexto. Veja o componente Connector por exemplo:
https://github.com/gaearon/react-redux/blob/master/src/components/createConnector.js

Eu não te sigo. Estou perfeitamente bem para acessar a loja usando @connect. Estou procurando uma maneira de vincular / chamar ações em um componente sem usar @connect.

A função dispatch é um método da loja. Se você precisar dele em seu componente, mas não quiser usar @connect , poderá acessar a loja por meio do contexto e usar seu método de envio:

class MyComponent extends Compoenent {
    static contextTypes = {
        store: React.PropTypes.object.isRequired,
    }

    render() {
         const { dispatch } = this.context.store
         const actions = bindActionCreators(headerActions, dispatch)
    }
}

Você deve passar o despacho do componente pai, ou a ação já vinculada ao despacho. Essa é _a_ maneira que acredito, o contexto também funciona, embora seja uma solução alternativa

Obrigado por explicar isso! Agora eu entendi.

Você pode passar dispatch para aquele componente manualmente como um suporte.

Também estou um pouco confuso com este problema.

Se bem entendi, precisamos ter dispatch disponível por meio de props de um componente (para ser capaz de despachar ações desse componente) ou dar aos criadores de ação já vinculados a dispatch , de um componente pai. Mas ambas as soluções requerem um componente pai connect ed para que o envio seja disponibilizado para ele por meio de seus adereços.

Portanto, no final, estamos apenas transferindo nossa dependência de dispatch para um componente pai. E como você faria isso se não houvesse esse pai connect ed?

Por exemplo, e se estivéssemos em uma hierarquia profunda de componentes:

Provider
  Router
    AppContainer
     ...
       ...
         ...
           TheComponentThatNeedDispatch

e se nenhum dos pais precisa se conectar ao estado redux?

Ter o envio disponibilizado para um componente como um efeito colateral do uso de connect parece nos colocar em uma situação em que é um pré-requisito ter um de seu pai connect ed para ser capaz de dispatch uma ação. Isso parece estranho, pois não deve haver qualquer correlação entre a habilidade de um componente em dispatch uma ação e a necessidade de usar connect para ler do estado redux.

Sei que ainda podemos acessar dispatch de um contexto de componente (via store.dispatch ), mas como não é a maneira recomendada ...

O que você acha disso?

Obrigado!

Isso parece estranho, pois não deve haver nenhuma correlação entre a capacidade de um componente de despachar uma ação e a necessidade de usar conectar para ler do estado redux.

Por que é estranho? Pense em connect como “conectar-se ao Redux” e não apenas “conectar-se a uma parte do estado Redux”. Então faz sentido: para ser capaz de dispatch qualquer componente em si, ou algum seu pai, precisa ser connect() ed para Redux.

Com a API React Redux atual, não é conveniente conectar-se ao Redux sem também selecionar o estado. Você teria que escrever algo como connect(state => ({}))(MyComponent) (return empty object). É por isso que estávamos explorando APIs alternativas para ele que tornam dispatch um cidadão de primeira classe e planejamos adicionar uma API para obter “apenas dispatch ”.

Isso faz sentido?

Pense em conectar como “conectar ao Redux” e não apenas “conectar a uma parte do estado Redux”.

Totalmente faz sentido agora :).

Mal posso esperar para usar a nova API .

Muito obrigado, usar Redux é uma benção!

Isso parece ótimo!

@gaearon ei Dan, você pode me dar uma dica sobre como usar bindActionCreators com componentes aninhados:

"AddProduct" - conectado ao redux (veja abaixo)
__ "Lista de produtos"
____ "ProductView".

Quero que o ProductView possa despachar, mas apenas o mestre "Adicionar produto" tem:

function appState(state) {
    return {...};
}

export default connect(appState)(AddProduct)

Você pode connect() o componente aninhado também, ou você pode passar dispatch para ele como um prop.

obrigado Dan, realmente agradeço sua ajuda. realmente amo redux também!

outra pergunta se eu puder:
posso colocar solicitações de postagem de API do servidor de dentro do próprio redutor?
quando faço POST no banco de dados (mongodb), o item obtém um "_id" que eu gostaria de ter no meu estado redux. o envio deve ser colocado como um retorno de chamada para a solicitação POST?

obrigado

posso colocar solicitações de postagem de API do servidor de dentro do próprio redutor?

Definitivamente não, os redutores devem ser funções puras e livres de quaisquer efeitos colaterais.

quando faço POST no banco de dados (mongodb), o item obtém um "_id" que eu gostaria de ter no meu estado redux. o envio deve ser colocado como um retorno de chamada para a solicitação POST?

Sim.

Consulte este guia: http://rackt.github.io/redux/docs/advanced/AsyncActions.html

obrigado Dan, funciona muito bem (y)

se estou usando a renderização do servidor para, digamos, fazer o usuário atual logar,
posso pular a busca assíncrona da loja e fazê-lo no servidor?

Se eu estiver usando a renderização do servidor para, digamos, obter o usuário atual conectado, posso pular a busca assíncrona da loja e fazê-lo no servidor?

Você pode criar armazenamento no servidor, aguardar a conclusão das ações assíncronas e, em seguida, renderizar.
Veja também http://rackt.github.io/redux/docs/recipes/ServerRendering.html

Provavelmente estou perdendo algo crítico, mas por que não aplicar apenas parcialmente o despacho em todos os criadores de ação durante a inicialização do aplicativo? Então você poderia ligar de qualquer lugar sem se preocupar em como obter uma referência para despachar.

@mpoisot : porque você teria que saber, importar, ligar e distribuir _todo_ criador de ação potencial na inicialização do aplicativo. Entre outras coisas, isso não ajuda em situações como divisão de código.

@markerikson Eu já tenho que conhecer todos os redutores no app init para que possa chamar createStore . Estou imaginando ligar para bindAllActionCreators(store.dispatch) na próxima linha.

É verdade que você pode acabar puxando um pouco mais código do que realmente usa. Mas se isso for uma preocupação, não faça assim. Ou divida seus conjuntos de redutores (redutor + criadores de ação + seletores) em pacotes menores e mais focados. Ou apenas vincule alguns criadores de ação.

Existe alguma penalidade de desempenho terrível para fazer isso? Ou de alguma forma leva a um terrível caminho para o inferno da codificação?

@mpoisot : não há realmente nenhum problema de desempenho que eu possa imaginar. No entanto, ele limita a testabilidade e a reutilização.

O uso adequado de connect resulta em uma abordagem de injeção de dependência leve. Um componente "simples" só sabe que está recebendo alguma função como um suporte e deve chamar essa função quando necessário. Na verdade, não se importa se é um retorno de chamada transmitido diretamente por algum componente pai ou um criador de ação pré-ligada. Isso significa que o componente pode ser reutilizado de maneira direta e também torna o teste muito mais fácil. Por outro lado, fazer algo como importar diretamente a loja resulta em vincular o componente a essa instância e implementação específica da loja, razão pela qual é desencorajado (de acordo com o Redux FAQ: http://redux.js.org/docs/faq/ StoreSetup.html # store-setup-multiple-stores). Se entendi corretamente o que você está perguntando, é basicamente a mesma situação que importar e fazer referência à loja diretamente.

Ah, bom ponto, aplicar despacho parcialmente em criadores de ação seria equivalente a importar a loja como um único, o que é desaprovado. Eu tentei descobrir o porquê e encontrei vários lugares onde Dan Abramov disse que uma loja de singleton é desaprovada devido à renderização do lado do servidor (que a maioria das pessoas nunca usará), mas estranho que ele nunca mencionou o teste (que todos deveriam fazer, ou pelo menos se sentir mal por não fazer).

O que me faz pensar, com certeza você pode criar um objeto singleton de loja cuja loja pode ser atualizada com diferentes lojas para teste? Em vez de exportar diretamente a própria loja, devolva-a por meio de StoreSingleton.getStore() e altere a loja atual por meio de StoreSingleton.setStore() . Portanto, em termos de teste, você não estaria preso a uma única instância de uma loja. No entanto, posso ver por que isso não funcionaria em um ambiente de servidor encadeado.

@mpoisot Você acabou de descrever o que Provider / Connect faz. ;)

Freqüentemente, tenho um componente "burro" que um dia precisa ser despachado (ou uma ação pré-limitada). Então, eu o envolvo em conectar, ou talvez passe coisas de um componente que está conectado. Muitas vezes parece que há muita fiação e adereços passando apenas para evitar o envio acessível globalmente. Para mapStateToProps (), há ganhos de desempenho maravilhosos com Provider / connect (). E para reutilizar componentes burros, definitivamente faz sentido extrair lógica de despacho para o componente de ordem superior.

Mas já atingi esse cenário várias vezes para me perguntar por quê. E parece que a resposta é "para que algum dia você possa fazer renderização do lado do servidor". (Por favor me corrija se eu estiver errado).

Agora, connect() dá-lhe apenas o dispatch método de Provider 's loja, mas no futuro, talvez isso pode mudar. Podemos adicionar funcionalidade extra, como integração com ferramentas de desenvolvimento para informar qual componente React foi a fonte de uma ação que está sendo disparada, por exemplo. Ou alguém pode fazer um componente que aprimora a loja de outra maneira, levando a loja no contexto, aprimorando-a de alguma forma e, em seguida, passando essa loja aprimorada para a árvore de componentes. Se você vinculou seus criadores de ação a uma loja de singleton, potencialmente estaria chamando o dispatch errado.

Sim, alguns dos experimentos de "componente / estado encapsulado" que vi funcionam exatamente com essa abordagem. O interessante dessa ideia é que os outros componentes não precisam saber que nada mudou, uma vez que os componentes do wrapper ainda estão apenas capturando this.context.store e usando seus dispatch conforme necessário.

Eu estive em ambos os lados disso -

Em meu aplicativo principal, tratamos a loja como um singleton e usamos uma função getStore() disponível globalmente. Não usamos renderização do lado do servidor. Ainda usamos Provider e connect para mapStateToProps .

Por outro lado, desenvolvi um aplicativo experimental onde envolvo a loja (como @markerikson mencionados) em vários lugares dentro da hierarquia de componentes, e ambos dispatch e getState podem mudar em todo o aplicativo, o que significa que eles _têm_ que ser conectados em todo o aplicativo (seja via context ou props ) e não podem ser únicos.

Dependendo do que você deseja realizar, acho que as coisas da loja de singleton são viáveis, mas pessoalmente estou me afastando disso aos poucos.

Sim. Você certamente _pode_ tratar a loja como um singleton acessível, mas isso o limita, não é considerado idiomático e não vejo nenhuma vantagem real em fazer isso.

Obrigado, ótimo feedback. Alguém pode postar um link mostrando alguns pacotes de loja?

@mpoisot

Aqui está um dos comentários de Dan que ilustra como embrulhar dispatch pode ser útil:

https://github.com/reactjs/redux/issues/822#issuecomment -145271384

Você pode ter que ler todo o tópico para entender melhor qual é a motivação.

Em particular, observe como dispatch é empacotado (eu destaquei):

wrappeddispatch

Desculpe por sequestrar este antigo post, pois estou enfrentando uma confusão semelhante, desejando despachar ações sem conectar.

export default class App extends Component {
...
  componentDidMount() {
    //registering event listener
    BackgroundGeolocation.on('location', this.onLocation, this.onError);
  }

  onLocation(location) {
   //wishing to dispatch an action to update variable in store
  }

  render() {
    return (
      <Provider store={store}>
        <AlertProvider>
          <MainNavigator screenProps={{ isConnected: this.state.isConnected }} />
        </AlertProvider>
      </Provider>
    );
  }
}

Pelo que entendi, não posso me conectar à loja neste componente porque estamos apenas configurando e passando a loja para Provider . Como eu possivelmente despacharia uma ação no meu evento onLocation ?

@isaaclem : bem, nesse caso específico, você _têm_ uma referência à loja, então poderia ligar diretamente para store.dispatch(someAction) .

A outra opção seria reestruturar sua estrutura de componentes de forma que algum componente _dentro_ do <Provider> faça o comportamento de geolocalização em segundo plano e também possa ser conectado.

Esta página foi útil?
0 / 5 - 0 avaliações