Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get simple dispatch from this.props using connect w/ Redux?

I've got a simple React component that I connect up (mapping a simple array/state over). To keep from referencing the context for store I'd like a way to get "dispatch" directly from props. I've seen others using this approach but don't have access to this for some reason :)

Here are the versions of each npm dependency I'm using currently

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

Here is the component w/ connect method

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

If you need to see the reducer (nothing exciting but here it is)

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

If you need to see how my router is configured w/ redux

const createStoreWithMiddleware = applyMiddleware(       thunk )(createStore);  const store = createStoreWithMiddleware(reducers);  var Route = (   <Provider store={store}>     <Router history={createBrowserHistory()}>       {Routes}     </Router>   </Provider> ); 

update

looks like if I omit my own dispatch in the connect (currently above I'm showing fetchUsers), I'd get dispatch free (just not sure if this is how a setup w/ async actions would work usually). Do people mix and match or is it all or nothing?

[mapDispatchToProps]

like image 688
Toran Billups Avatar asked Dec 24 '15 22:12

Toran Billups


People also ask

How do you get a Functionu component from Redux state?

redux state can be accessed as prop in a function by using below format. 1: import { connect } from 'react-redux'; // log accepts logMessage as a prop function log(props) { const { environment, logMessage } = props; console. debug('environment' + environment + logMessage ); .... }

How do you dispatch actions in a functional component?

There are two ways to dispatch actions from functional components: Using mapDispatachToProps function with connect higher order component, same as in class based components. Using useDispatch hook provided by react-redux . If you want to use this hook, then you need to import it from the react-redux package.


2 Answers

By default mapDispatchToProps is just dispatch => ({ dispatch }).
So if you don't specify the second argument to connect(), you'll get dispatch injected as a prop in your component.

If you pass a custom function to mapDispatchToProps, you can do anything with the function.
A few examples:

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

To save you some typing Redux provides bindActionCreators() that lets you turn this:

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

into this:

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

or even shorter when prop names match action creator names:

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

If you'd like you can definitely add dispatch there by hand:

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

There's no official advise on whether you should do this or not. connect() usually serves as the boundary between Redux-aware and Redux-unaware components. This is why we usually feel that it doesn't make sense to inject both bound action creators and dispatch. But if you feel like you need to do this, feel free to.

Finally, the pattern you are using right now is a shortcut that's even shorter than calling bindActionCreators. When all you do is return bindActionCreators, you can omit the call so instead of doing this:

// injects increment and decrement function mapDispatchToProps(dispatch) {   return bindActionCreators({ increment, decrement }, dispatch); }  export default connect(   mapStateToProps,   mapDispatchToProps )(App); 

can be written as this

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

However you'll have to give up that nice short syntax whenever you want something more custom, like passing dispatch as well.

like image 114
Dan Abramov Avatar answered Oct 11 '22 11:10

Dan Abramov


You can usually mix and match based on what you'd like.

You can pass dispatch on as a prop if that is what you want:

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

I'm not sure how fetchUsers is used (as an async function?), but you would usually use something like bindActionCreators to auto-bind dispatch and then you would not have to worry about using dispatch directly in connected components.

Using dispatch directory sort of couples the dumb, stateless component with redux. Which can make it less portable.

like image 39
Davin Tryon Avatar answered Oct 11 '22 11:10

Davin Tryon