O que é mapDispatchToProps?

358

Eu estava lendo a documentação da biblioteca Redux e ela tem este exemplo:

Além de ler o estado, os componentes do contêiner podem despachar ações. De maneira semelhante, você pode definir uma função chamada mapDispatchToProps()que recebe o dispatch()método e retorna adereços de retorno de chamada que deseja injetar no componente de apresentação.

Isso realmente não faz sentido. Por que você precisa mapDispatchToPropsquando você já tem mapStateToProps?

Eles também fornecem este exemplo de código útil:

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

Alguém pode explicar em termos leigos o que é essa função e por que é útil?

Code Whisperer
fonte

Respostas:

577

Sinto que nenhuma das respostas se cristalizou por que mapDispatchToPropsé útil.

Na verdade, isso só pode ser respondido no contexto do container-componentpadrão, que achei melhor entendido pela primeira leitura: Componentes do contêiner e depois uso com o React .

Em poucas palavras, você componentsdeve se preocupar apenas em exibir coisas. O único lugar em que eles devem obter informações são seus objetos .

Separado de "exibir coisas" (componentes) está:

  • como você exibe as coisas,
  • e como você lida com eventos.

É para isso que containersservem.

Portanto, um "bem projetado" componentno padrão se parece com isso:

class FancyAlerter extends Component {
    sendAlert = () => {
        this.props.sendTheAlert()
    }

    render() {
        <div>
          <h1>Today's Fancy Alert is {this.props.fancyInfo}</h1>
          <Button onClick={sendAlert}/>
        </div>
     }
}

Veja como este componente recebe a informação ele exibe a partir de adereços (que veio a partir da loja redux via mapStateToProps) e também obtém sua função de ação de seus adereços: sendTheAlert().

É aí que mapDispatchToPropsentra: no correspondentecontainer

// FancyButtonContainer.js

function mapDispatchToProps(dispatch) {
    return({
        sendTheAlert: () => {dispatch(ALERT_ACTION)}
    })
}

function mapStateToProps(state) {
    return({fancyInfo: "Fancy this:" + state.currentFunnyString})
}

export const FancyButtonContainer = connect(
    mapStateToProps, mapDispatchToProps)(
    FancyAlerter
)

Eu me pergunto se você pode ver, agora que é o container 1 que sabe sobre redux e despacho e armazenamento e estado e ... coisas.

O componentno padrão, FancyAlerterque faz a renderização não precisa saber nada disso: ele obtém seu método para chamar no onClickbotão, por meio de seus props.

E ... mapDispatchToPropsforam os meios úteis que o redux fornece para permitir que o contêiner passe facilmente essa função para o componente embrulhado em seus adereços.

Tudo isso se parece muito com o exemplo do todo em documentos e outra resposta aqui, mas tentei lançá-lo à luz do padrão para enfatizar o porquê .

(Nota: você não pode usar mapStateToPropscom a mesma finalidade e mapDispatchToPropspelo motivo básico ao qual não tem acesso dispatchinterno mapStateToProp. Portanto, não pode usar mapStateToPropspara dar ao componente agrupado um método que usa dispatch.

Não sei por que eles escolheram dividi-lo em duas funções de mapeamento - poderia ter sido mais organizado ter mapToProps(state, dispatch, props) uma função do IE para fazer as duas coisas!


1 Observe que eu deliberadamente nomeei explicitamente o contêiner FancyButtonContainer, para destacar que é uma "coisa" - a identidade (e, portanto, a existência!) Do contêiner como "uma coisa" às vezes se perde na abreviação

export default connect(...) ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

sintaxe mostrada na maioria dos exemplos

GreenAsJade
fonte
5
Eu ainda gostaria de saber: E a funcionalidade necessária não pôde ser tratada chamando diretamente store.dispatch de dentro de um componente?
Pixelpax #
8
@ user2130130 Nenhuma. É sobre um bom design. Se você acoplou seu componente ao store.dispatch, quando decide fatorar o redux ou deseja usá-lo em algum lugar que não seja baseado em redxu (ou em alguma outra coisa em que não consigo pensar agora), você está preso a um muitas mudanças. Sua pergunta é generalizada para "por que nos preocupamos com 'boas práticas de design' - você obtém a mesma funcionalidade no entanto codifica-a". O mapDispatchToProps é fornecido para que você possa escrever componentes bem projetados e bem dissociados.
precisa saber é o seguinte
4
O que realmente não entendi aqui é o que é: ALERT_ACTIONa função de ação ou a typeque é retornada da função de ação? : / so baffed
Jamie Hutber 3/17/17
11
@JamieHutber Estritamente, ALERT_ACTION não tem nada a ver com esta pergunta. É um argumento válido para despachar e, como acontece, no meu código, vem de um "construtor de ações", que retorna um objeto que o despacho usa, conforme descrito redux.js.org/docs/api/Store.html#dispatch . O ponto principal aqui é que, como chamar a expedição, é descrito no contêiner e repassado aos componentes de componentes. O componente só obtém funções de seus objetos, em nenhum outro lugar. A maneira "errada" de fazê-lo nesse padrão seria passar a expedição para o componente e fazer a mesma chamada no componente.
GreenAsJade 8/17/17
11
Se você tiver vários criadores de ações que precisam ser passados ​​para o componente filho como acessórios, uma alternativa é envolvê-los com bindActionCreators dentro de mapDispatchToProps (consulte redux.js.org/docs/api/bindActionCreators.html ); outra alternativa é simplesmente fornecer um objeto de criadores de ação para connect (), por exemplo, connect (mapStateToProps, {actioncreators}), o react-redux os envolverá com dispatch () para você.
dave
81

É basicamente uma abreviação. Então, em vez de ter que escrever:

this.props.dispatch(toggleTodo(id));

Você usaria mapDispatchToProps conforme mostrado no seu código de exemplo e, em seguida, gravaria em outro lugar:

this.props.onTodoClick(id);

ou, mais provavelmente, nesse caso, você teria isso como manipulador de eventos:

<MyComponent onClick={this.props.onTodoClick} />

Há um vídeo útil de Dan Abramov sobre isso aqui: https://egghead.io/lessons/javascript-redux-generating-containers-with-connect-from-react-redux-visibletodolist

hamstu
fonte
Obrigado. Também estou me perguntando, como o despacho é adicionado aos adereços em primeiro lugar?
Code Whisperer
14
Se você não fornecer sua própria mapDispatchfunção, o Redux usará um padrão. Essa mapDispatchfunção padrão simplesmente pega a dispatchreferência da função e a fornece como this.props.dispatch.
markerikson
7
O método de despacho é passado pelo componente
Diego Haz
55

mapStateToProps()é um utilitário que ajuda seu componente a obter o estado atualizado (que é atualizado por alguns outros componentes),
mapDispatchToProps()é um utilitário que ajuda seu componente a disparar um evento de ação (ação de despacho que pode causar alteração no estado do aplicativo)

Saisurya Kattamuri
fonte
23

mapStateToProps, mapDispatchToPropse connectfrom react-reduxlibrary fornece uma maneira conveniente de acessar suas funções statee dispatchsua loja. Então, basicamente, o connect é um componente de ordem superior, você também pode pensar como invólucro, se isso fizer sentido para você. Portanto, toda vez que você statealterar mapStateToProps, será chamado com o novo statee, posteriormente, à medida que você propsatualizar o componente, executará a função de renderização para renderizar o componente no navegador. mapDispatchToPropstambém armazena valores-chave no propsseu componente, geralmente eles assumem a forma de uma função. Dessa forma, você pode acionar statealterações do seu componente onClick,onChange eventos.

Dos documentos:

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

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

function toggleTodo(index) {
  return { type: TOGGLE_TODO, index }
}

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

Além disso, verifique se você está familiarizado com as funções sem estado de reagir e com os componentes de ordem superior

Vlad Filimon
fonte
Então despacho é basicamente como evento?
Code Whisperer
2
Pode estar relacionado ao evento, o envio é apenas uma função e a única maneira de alterar o estado do aplicativo . O mapStateToProps é uma maneira de expor a função de despacho da sua loja ao React Component. Observe também que o connect não faz parte do redux; na verdade, é apenas um utilitário e a biblioteca de redução de padrões chamada react-redux para trabalhar com o react e o redux. Você pode obter o mesmo resultado sem o react-redux se passar sua loja do componente de reação raiz para os filhos.
Vlad Filimon
3

mapStateToProps recebe o state e propse permite que você extraia adereços do estado para passar para o componente.

mapDispatchToProps recebe dispatch e propse destina-se a vincular criadores de ação a serem despachados, portanto, quando você executa a função resultante, a ação é despachada.

Acho que isso só evita que você precise fazer dispatch(actionCreator()) dentro do seu componente, facilitando a leitura.

https://github.com/reactjs/react-redux/blob/master/docs/api.md#arguments

Harry Moreno
fonte
Obrigado, mas qual é o valor do dispatchmétodo? De onde isso vem?
Code Whisperer
ah O envio basicamente faz com que a ação inicie o fluxo unidirecional de redux / flux. As outras respostas parecem ter respondido à sua pergunta melhor do que isso.
Harry Moreno
Além disso, o método de despacho vem do aprimoramento fornecido pelo <Provider/>próprio. Ele faz sua própria mágica de componente de ordem superior para garantir que a expedição esteja disponível nos componentes filhos.
Ricardo Magalhães
3

Agora, suponha que haja uma ação para redux como:

export function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

Quando você o importa,

import {addTodo} from './actions';

class Greeting extends React.Component {

    handleOnClick = () => {
        this.props.onTodoClick(); // This prop acts as key to callback prop for mapDispatchToProps
    }

    render() {
        return <button onClick={this.handleOnClick}>Hello Redux</button>;
    }
}

const mapDispatchToProps = dispatch => {
    return {
      onTodoClick: () => { // handles onTodoClick prop's call here
        dispatch(addTodo())
      }
    }
}

export default connect(
    null,
    mapDispatchToProps
)(Greeting);

Como o nome da função diz mapDispatchToProps(), mapeie a dispatchação para adereços (adereços do nosso componente)

Portanto, prop onTodoClické uma chave para a mapDispatchToPropsfunção que delega para enviar ações addTodo.

Além disso, se você quiser cortar o código e ignorar a implementação manual, poderá fazer isso,

import {addTodo} from './actions';
class Greeting extends React.Component {

    handleOnClick = () => {
        this.props.addTodo();
    }

    render() {
        return <button onClick={this.handleOnClick}>Hello Redux</button>;
    }
}

export default connect(
    null,
    {addTodo}
)(Greeting);

O que exatamente significa

const mapDispatchToProps = dispatch => {
    return {
      addTodo: () => { 
        dispatch(addTodo())
      }
    }
}
Conheça Zaveri
fonte