I want to use both this.props.dispatch and my own defined actions creators to dispatch actions from my react view component.
However, I can not access this.props.dispatch if I pass in mapDispatchToProps to connect(). So, this is the workaround that I came up with:
function mapDispatchToProps(dispatch) {
return bindActionCreators({
updateRackGroup,
cloneRackGroup,
onSelectRack,
onCloneRack,
valRackChange,
valRackAdd,
onRackAction,
valRackUpdate,
dispatch
}, dispatch);
}
function mapStateToProps({
tempRackStates,
rackGrpsAdded,
invalidFields
}) {
return {
tempRackStates,
rackGrpsAdded,
invalidFields
}
}
export default connect(mapStateToProps, mapDispatchToProps)(RackgroupGenerator);
I am not happy with it, as "it wraps dispatch within dispatch".
So is there an elegant way to gain access to the raw dispatch method under this scenario?
You don't need to pass dispatch do bindActionCreators. Just merge it to the bound action creators
function mapDispatchToProps(dispatch) {
const actionCreators = bindActionCreators({
actionCreator1,
actionCreator2,
}, dispatch);
return { ...actionCreators, dispatch };
}
// or without the additional assignment
return {
dispatch,
...bindActionCreators({ actionCreator1, actionCreator2 }, dispatch)
};
BTW I didn't test it for sure, but I also think you shouldn't "wrap dispatch with dispatch" because I believe it can lead to strange behavior such as unwillingly dispatching actions more than once.
Related
I'm using react-redux & redux-thunk for my project.
I have to inject my actions to a component by using connect.
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
My task is one level up. I don't want just to inject multiple actions in this form:
{
doThis(),
doThat()
}
But in this form:
{
this: {
doThis1(),
doThis2()
}
that: {
doThat()
}
}
So basically my problem is that I want to dispatch multiple action-creator files because I want them organized as such.
I tried this version which obviously doesn't work because dispatch is not injected in each Thunk Action Creator:
import * as actions from './actions'
const mapDispatchToProps = (dispatch) => {
return {
dataActions: {
...actions.dataActions
}
};
}
export default connect(null, mapDispatchToProps)(Component);
So my final question is:
Am I even supposed to use Redux this way? Can I organize my files this way, if so how?
If instead of having one property per action creator, you want to structure your bound action creators in a couple of properties that each contain a group of action creators, you can do something like this:
import { bindActionCreators, .. } from 'redux';
..
const mapDispatchToProps = (dispatch) => {
return {
dataActions: bindActionCreators(actions.dataActions, dispatch),
otherActions: bindActionCreators(actions.otherActions, dispatch),
..
};
};
The first argument to bindActionCreators is an object containing action-creator functions (e.g. an imported module that exports only action creators). In your actual component, you should then be able to use this.props.dataActions.someDataAction(..).
If the question is just about whether you can keep different action creators in different files, you might not even want to group the action creators and just do this:
return {
...bindActionCreators(actionCreatorsFromOneModule, dispatch),
...bindActionCreators(actionCreatorsFromAnotherModule, dispatch),
..
};
All:
I am pretty new to Redux, when I follow its Reddit API example, there is one code snippet confuse me so much:
In AsyncApp.js, there is:
componentDidMount() {
const { dispatch, selectedSubreddit } = this.props
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
I wonder where the dispatch and selectedSubreddit get bind to this.props?
Thanks
That example is using the connect() function from react-redux to inject certain parts of the Redux state and the store's dispatch() function as props in that component. See the 'Usage With React' part of the Redux docs for more information.
For example:
App.js:
export class App extends Component {
//...
}
function mapStateToProps(state) {
const { selectedReddit, postsByReddit } = state
const {
isFetching,
lastUpdated,
items: posts
} = postsByReddit[selectedReddit] || {
isFetching: true,
items: []
}
return {
selectedReddit,
posts,
isFetching,
lastUpdated
}
}
export default connect(mapStateToProps)(App)
The connect() function here is taking the mapStateToProps() function above to inject the appropriate parts of the Redux state as props in the <App /> component. The keys of the object returned by mapStateToProps() correspond to the names of the props injected, and the corresponding values are the values of those injected props.
connect() can also take a second argument, matchDispatchToProps(), which can be used to inject specific action dispatch functions as props in your component. Whether or not you supply any arguments to connect(), it will inject your Redux store's dispatch() function as a prop called dispatch.
These connected container components receive state updates from the store, so when your Redux state changes, the connected container components will receive new props accordingly.
I've seen the argument for separating actions and reducers because they have a many-to-many relationship.
I don't think that actually applies in Redux though. Because there's only 1 datastore, actions to reducers should be 1-to-many.
Typically reducers apply to a specific change for a specific datastore.
MY_ACTION = "MY_ACTION"
function reducer(state, action) {
switch(action.type) {
case MY_ACTION: // stuff with my action to create new state
default: return state
}
}
We can combine multiple reducers with combineReducers so why not define the handler for an action with the action itself.
For instance
class Action {
constructor(type) {
this.type = type
this.handlers = []
}
add_handler(handler) {
this.handlers += handler
}
get_reducer() {
reducer = combineReducers(this.handlers)
return (state, action) => {
if(action.type == this.type) {
return reducer(state, action)
}
return state
}
}
}
With the "ducks" pattern, we end up putting the main reducers in the same module as the action declaration.
Is there any reason to keep reducers + actions separate with redux?
The main reason for separating the action creators from the reducer function is that the reducer function must be a pure function. If you wanted to do something in an action creator, like an asynchronous API call for instance, then you could not put this in the reducer. There's a great explanation on this here.
I'm trying to build keyboard shortcut support into my React/Redux app in an idiomatic React/Redux way. The way I am planning to do this is to have the following action creator and associated action:
registerShortcut(keyCode, actionCreatorFuncReference)
The reducer would then update a registeredShortcuts object in the redux store with a mapping of keyCodes to actionCreatorFuncReferences. Then my root component would listen for keyup and see if there is an associated keyCode registered and if so, then dispatch the mapped action via the action creator function reference.
However, this would be the first time I am storing function references in my Redux store. To date, I've only had objects with keys with vanilla values (strings, ints, etc).
The Redux docs says:
You should do your best to keep the state serializable. Don’t put anything inside it that you can’t easily turn into JSON.
Does this suggest it's a bad idea to store such function references in my Redux store? If so, what is a better way to accomplish what I'm trying to do in React/Redux?
An alternative approach is just to store the mapping of keyCodes and function references in the root react component itself, but that didn't feel very Redux-like since now the application state is not in the Redux store.
No, you should not store function references in the redux store. They are not serializable, and as you mentioned state should be serializable at all times. The most redux-friendly approach I can think of is just to keep the map of hotkeys to their actionCreatorFuncNames.
TL;DR: You don't. The store state must be serializable at all times (as Nathan answered).
The Redux way is via enhancers, or the Redux-Observable way via dependencies.
NL;PR: Based on the Redux docs example, what you want is to pass the reference in your action(1), ignore it your reducer(2) and use it in your enhancer(3):
//... in your action:
const data={val:1}, ref=()=>{};
const action = {type:'ACTION_WITH_REF', data, ref}; //(1)
//... in your reducer:
case 'ACTION_WITH_REF':
return {...state, data: action.data}; //(2)
//... and in your enhancer:
import { createStore, applyMiddleware } from 'redux';
import reducers from './reducers';
export const myRefStore= {};
function refHandler({ getState }) {
return next => action => {
switch(action.type){
// this can be done more elegantly with a redux-observable
case 'ACTION_WITH_REF':
myRefStore.aRef = action.ref; // (3)
break;
}
// be sure to maintain the chain of the store
const returnValue = next(action);
// otherwise, your midddeware will break the store
return returnValue;
};
}
const store = createStore(
reducers,
initialState,
applyMiddleware(refHandler)
);
Note: As far as there are no side-effects in your enhancers, you are good to go. Be aware that you could have obtained the refs directly in the reducers, but such an approach keeps the reference at the reducer-level and misses the point of combineReducers(). With an enhancer, you keep them all in one place(myRefStore).
One final observation is that a redux store is not an any-data store but a state store, thus why we need to handle functions and other non-state related stuff in enhancers. You can leverage the enhancer backbone to Redux-Observable and inject myRefStore via dependencies.
I'm new to redux, but the way I see it, you could pass the key code and an action type.
Then a reducer could be listening for that action type and make changes accordingly.
Here is an example using the library Mousetrap:
// On your Container
function registerShortcut(element, dispatch, keyCode, actionType) {
Mousetrap(element).bind(keyCode, function(e) {
dispatch({
type: actionType,
payload: {
keyCode: keyCode,
event: e
}
});
});
});
mapDispatchToProps = function(dispatch) {
return {
onMount: function(element) {
registerShortcut(element, dispatch, ['command+f', 'ctrl+f'], 'OPEN_SEARCH');
},
onUnmount: function(element) {
Mousetrap(element).unbind(['command+f', 'ctrl+f']);
}
};
};
// On your Component
componentDidMount() {
onMount(ReactDOM.findDOMNode(this));
};
componentWillUnmount() {
onUnmount(ReactDOM.findDOMNode(this));
};
// On your reducer
function reducer(oldState, action) {
if (action.type == 'OPEN_SEARCH') {
//... make changes ...//
return newState;
}
return oldState;
};
This way, keyboard shortcuts will dispatch an action. The reducer will make the changes necessary to the state. And finally, the application can re-render.
this is just a question,
I'd love to double check if I'm doing things right. I'm coming from ages of different frameworks, and I def want to avoid bad practices in the early stage.
I'm using this boilerplate: https://github.com/erikras/react-redux-universal-hot-example
and I'm writing in ES7.
I created:
- 1 reducer: earlyUser.js
- 1 container: landingPage.js
- 1 component: registrationForm.js
In the landingPage, I'm including the methods from reducer in this way:
import { saveEmail, savePreferences, checkEmailExists } from 'redux/modules/earlyUser';
and I declare some handles
handleSubmitEmail(data) {
this.props.saveEmail(data);
}
handleSubmitPreferences(data) {
//some data manipulation
this.props.savePreferences(data);
}
and in the JSX part I just pass to my component the handlers:
<registrationForm submitEmail= {::this.handleSubmit} etc... >
Now inside the component, I linked the form submission to this handler:
submitEmail() {
if (this.validateEmail(this.state.email)) {
this.props.submitEmailHandler(this.state);
}
}
Now my question is, where should I attach the .then and .catch of the promise returned ?
Ideally I'd like to do inside the component.js something like
this.props.submitEmailHandler(this.state).then( function emailRegisterCallback(){
// move to next step
}).catch( function errorHandler(error){
// there was an error
}
Is that correct?
Also, is there the right syntax to handle promises in ES7 ?
You normally handle the async aspects in your action creators:
See this code from the async redux example:
function fetchPosts(reddit) {
return dispatch => {
dispatch(requestPosts(reddit));
return fetch(`http://www.reddit.com/r/${reddit}.json`)
.then(response => response.json())
.then(json => dispatch(receivePosts(reddit, json)));
};
}
When the promise resolves, you should dispatch another action to update the state with the successful result or the error.