¿Cómo obtener un envío simple de this.props usando connect w / Redux?

107

Tengo un componente React simple que conecto (mapeando una matriz / estado simple). Para evitar hacer referencia al contexto de la tienda, me gustaría una forma de obtener "despacho" directamente de los accesorios. He visto a otros usar este enfoque pero no tienen acceso a esto por alguna razón :)

Aquí están las versiones de cada dependencia npm que estoy usando actualmente

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

Aquí está el componente con método de conexión

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);

Si necesita ver el reductor (nada emocionante, pero aquí 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;
};

Si necesita ver cómo está configurado mi enrutador con redux

const createStoreWithMiddleware = applyMiddleware(
      thunk
)(createStore);

const store = createStoreWithMiddleware(reducers);

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

actualizar

Parece que si omito mi propio envío en la conexión (actualmente arriba, estoy mostrando fetchUsers), obtendría el envío gratis (pero no estoy seguro de si así es como una configuración con acciones asíncronas funcionaría normalmente). ¿La gente se mezcla y combina o es todo o nada?

[mapDispatchToProps]

Toran Billups
fuente

Respuestas:

280

Por defecto mapDispatchToPropses solo dispatch => ({ dispatch }).
Entonces, si no especifica el segundo argumento connect(), se le dispatchinyectará como accesorio en su componente.

Si pasa una función personalizada a mapDispatchToProps, puede hacer cualquier cosa con la función.
Algunos ejemplos:

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

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

Para ahorrarle algo de escritura, Redux proporciona bindActionCreators()que le permite convertir esto:

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

dentro de esto:

import { bindActionCreators } from 'redux';

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

o incluso más corto cuando los nombres de accesorios coinciden con los nombres de los creadores de acciones:

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

Si lo desea, definitivamente puede agregarlo dispatcha mano:

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

No hay ningún consejo oficial sobre si debe hacer esto o no. connect()generalmente sirve como el límite entre los componentes compatibles con Redux y los que no lo son. Es por eso que generalmente sentimos que no tiene sentido inyectar tanto creadores de acción vinculados como dispatch. Pero si siente que necesita hacer esto, no dude en hacerlo.

Finalmente, el patrón que estás usando ahora es un atajo que es incluso más corto que llamar bindActionCreators. Cuando todo lo que haga es regresar bindActionCreators, puede omitir la llamada en lugar de hacer esto:

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

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

se puede escribir como esto

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

Sin embargo, tendrá que renunciar a esa agradable sintaxis corta siempre que desee algo más personalizado, como pasar dispatchtambién.

Dan Abramov
fuente
2
@DanAbramov por cierto, ¿el segundo parámetro es bindActionCreators(actionCreators, dispatch)opcional? Noté que en su código en la // es7 spread syntaxlínea, dispatchno se pasa abindActionCreators
yonasstephen
¿Qué es el método de incremento interior?
Khurshid Ansari
La sintaxis de propagación de es7 debe ser function mapDispatchToProps(dispatch) { return { ...bindActionCreators({ increment, decrement }), dispatch }; }
negra
6

Por lo general, puede mezclar y combinar según lo que desee.

Usted puede pasar dispatchcomo un puntal si eso es lo que quiere:

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

No estoy seguro de cómo fetchUsersse utiliza (como una función asíncrona?), Pero que generalmente se usa algo como bindActionCreatorspara auto-bind envío y entonces no tendría que preocuparse acerca de cómo utilizar dispatchdirectamente en los componentes conectados.

El uso de dispatchdirectorios empareja el componente tonto y sin estado con redux. Lo que puede hacer que sea menos portátil.

Davin Tryon
fuente
3
Lo que sugieres no funcionará. Obtendrá una fetchUsersinyección suelta como apoyo. La notación de método abreviado solo funciona cuando pasa un objeto como segundo argumento. Cuando se pasa una función que tiene que llamar bindActionCreatorsa sí mismo: dispatch => ({ ...bindActionCreators({ fetchUsers }, dispatch), dispatch }).
Dan Abramov
¿Por qué difundir y pasar el envío directamente en bindActionCreators? dispatch => ( bindActionCreators({ dispatch, fetchUsers }, dispatch))
user3711421
2

Si bien podría pasar dispatchcomo parte de dispatchToProps, recomendaría evitar acceder al storeo dispatchdirectamente desde sus componentes. Parece que sería mejor para usted pasar un creador de acción vinculado en el segundo argumento de ConnectdispatchToProps

Vea un ejemplo que publiqué aquí https://stackoverflow.com/a/34455431/2644281 de cómo transmitir un "creador de acciones ya vinculado" de esta manera, sus componentes no necesitan saber directamente o depender de la tienda / despacho .

Perdón por ser breve. Actualizaré con más información.

Erik Aybar
fuente