Como obter despacho simples de this.props usando connect w / Redux?

107

Eu tenho um componente React simples que eu conecto (mapeando uma matriz / estado simples). Para evitar fazer referência ao contexto da loja, gostaria de uma maneira de obter "envio" diretamente dos adereços. Já vi outras pessoas usando essa abordagem, mas não têm acesso a ela por algum motivo :)

Aqui estão as versões de cada dependência de npm que estou usando atualmente

"react": "0.14.3",
"react-redux": "^4.0.0",
"react-router": "1.0.1",
"redux": "^3.0.4",
"redux-thunk": "^1.0.2"

Aqui está o componente com método de conexão

class Users extends React.Component {
    render() {
        const { people } = this.props;
        return (
            <div>
                <div>{this.props.children}</div>
                <button onClick={() => { this.props.dispatch({type: ActionTypes.ADD_USER, id: 4}); }}>Add User</button>
            </div>
        );
    }
};

function mapStateToProps(state) {
    return { people: state.people };
}

export default connect(mapStateToProps, {
    fetchUsers
})(Users);

Se você precisar ver o redutor (nada emocionante, mas aqui está)

const initialState = {
    people: []
};

export default function(state=initialState, action) {
    if (action.type === ActionTypes.ADD_USER) {
        let newPeople = state.people.concat([{id: action.id, name: 'wat'}]);
        return {people: newPeople};
    }
    return state;
};

Se você precisa ver como meu roteador está configurado com redux

const createStoreWithMiddleware = applyMiddleware(
      thunk
)(createStore);

const store = createStoreWithMiddleware(reducers);

var Route = (
  <Provider store={store}>
    <Router history={createBrowserHistory()}>
      {Routes}
    </Router>
  </Provider>
);

atualizar

parece que se eu omitir meu próprio despacho na conexão (atualmente acima estou mostrando fetchUsers), eu o obteria gratuitamente (só não tenho certeza se é assim que uma configuração com ações assíncronas normalmente funcionaria). As pessoas se misturam e combinam ou é tudo ou nada?

[mapDispatchToProps]

Toran Billups
fonte

Respostas:

280

Por padrão, mapDispatchToPropsé justo dispatch => ({ dispatch }).
Portanto, se você não especificar o segundo argumento para connect(), será dispatchinjetado como um suporte em seu componente.

Se você passar uma função personalizada para mapDispatchToProps, poderá fazer qualquer coisa com a função.
Alguns exemplos:

// inject onClick
function mapDispatchToProps(dispatch) {
  return {
    onClick: () => dispatch(increment())
  };
}

// inject onClick *and* dispatch
function mapDispatchToProps(dispatch) {
  return {
    dispatch,
    onClick: () => dispatch(increment())
  };
}

Para economizar um pouco de digitação, o Redux oferece bindActionCreators()que permite que você ligue:

// injects onPlusClick, onMinusClick
function mapDispatchToProps(dispatch) {
  return {
    onPlusClick: () => dispatch(increment()),
    onMinusClick: () => dispatch(decrement())
  };
}

nisso:

import { bindActionCreators } from 'redux';

// injects onPlusClick, onMinusClick
function mapDispatchToProps(dispatch) {
  return bindActionCreators({
    onPlusClick: increment,
    onMinusClick: decrement
  }, dispatch);
}

ou ainda mais curto quando os nomes prop correspondem aos nomes dos criadores de ação:

// injects increment and decrement
function mapDispatchToProps(dispatch) {
  return bindActionCreators({ increment, decrement }, dispatch);
}

Se desejar, você pode adicionar dispatchlá manualmente:

// injects increment, decrement, and dispatch itself
function mapDispatchToProps(dispatch) {
  return {
    ...bindActionCreators({ increment, decrement }), // es7 spread syntax
    dispatch
  };
}

Não há nenhum conselho oficial sobre se você deve fazer isso ou não. connect()geralmente serve como a fronteira entre os componentes que reconhecem Redux e os que não reconhecem Redux. É por isso que geralmente se sentem que não faz sentido para injetar ambos os criadores e ação encadernados dispatch. Mas se você sentir que precisa fazer isso, fique à vontade.

Finalmente, o padrão que você está usando agora é um atalho ainda mais curto do que chamar bindActionCreators. Quando tudo que você faz é retornar bindActionCreators, você pode omitir a chamada, em vez de fazer isso:

// injects increment and decrement
function mapDispatchToProps(dispatch) {
  return bindActionCreators({ increment, decrement }, dispatch);
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

pode ser escrito assim

export default connect(
  mapStateToProps,
  { increment, decrement } // injects increment and decrement
)(App);

No entanto, você terá que desistir dessa sintaxe curta sempre que quiser algo mais personalizado, como passar dispatchtambém.

Dan Abramov
fonte
2
@DanAbramov btw é o segundo parâmetro bindActionCreators(actionCreators, dispatch)opcional? Percebi em seu código na // es7 spread syntaxlinha, o dispatchnão é passado parabindActionCreators
yonasstephen
o que está dentro do método de incremento?
Khurshid Ansari
A sintaxe de propagação do es7 deve ser function mapDispatchToProps(dispatch) { return { ...bindActionCreators({ increment, decrement }), dispatch }; }
Preto
6

Normalmente, você pode misturar e combinar com base no que deseja.

Você pode passar dispatchcomo um adereço se é o que deseja:

export default connect(mapStateToProps, (dispatch) => ({
    ...bindActionCreators({fetchUsers}, dispatch), dispatch
}))(Users);

Eu não tenho certeza de como fetchUsersé utilizado (como uma função assíncrona?), Mas você normalmente usaria algo como bindActionCreatorsa auto-bind expedição e, em seguida, você não teria que se preocupar sobre como utilizar dispatchdiretamente em componentes conectados.

O uso de dispatchdiretório acopla o componente idiota e sem estado com redux. O que pode torná-lo menos portátil.

Davin Tryon
fonte
3
O que você está sugerindo não funcionará. Você receberá uma fetchUsersinjeção não ligada como um suporte. A notação de atalho só funciona quando você passa um objeto como o segundo argumento. Quando você passar uma função que você tem que chamar bindActionCreators-se: dispatch => ({ ...bindActionCreators({ fetchUsers }, dispatch), dispatch }).
Dan Abramov
Por que espalhar e passar despacho diretamente em bindActionCreators? dispatch => ( bindActionCreators({ dispatch, fetchUsers }, dispatch))
user3711421
2

Embora você possa passar dispatchpara baixo como parte de dispatchToProps, recomendo evitar acessar o storeou dispatchdiretamente de dentro de seus componentes. Parece que você ficaria melhor ao passar um criador de ação vinculada no segundo argumento de conexãodispatchToProps

Veja um exemplo que postei aqui https://stackoverflow.com/a/34455431/2644281 de como transmitir um "criador de ação já vinculado" desta forma, seus componentes não precisam saber diretamente ou depender da loja / expedição .

Desculpe ser breve. Vou atualizar com mais informações.

Erik Aybar
fonte