Redux - action gets dispatched, but doesn't work with the reducer - javascript

I've got a weird problem where I dispatch an action correctly, but the reducer doesn't get called. I'm using redux-thunk and everything is set up exactly the same how it's set up in one of my other projects. The action type matches, but nothing happens.
Also, if I put a console.log on the default case of the reducer, it gets logged 4 times every time I refresh the page, so I suppose the reducer gets loaded correctly.
export const submitForm = names => {
console.log(names); //This logs what it's supposed to log every time
// I press the submit button
return dispatch => {
dispatch({
type: "SUBMIT_FORM",
names
});
};
};
export default function form(state = {}, action) {
switch (action.type) {
case "SUBMIT_FORM":
console.log(action.names);
return Object.assign({}, state, {
names: action.names
});
default:
return state;
}
}
//My component is connected like this:
const mapStateToProps = state => ({});
const mapDispatchToProps = dispatch => ({
formActions: bindActionCreators(formActions, dispatch)
});
export default connect(mapStateToProps, mapDispatchToProps)(Home);

It seems a little strange to me that an action creator returns a function. Action creatorsnormally return an action, that is, an object. Async action creators returns functions. So, you can try refactor your action creator that will return a objetct, and then you pass the action creator to the dispatch as an argument.
export const submitForm = names => {
console.log(names); //This logs what it's supposed to log every time
// I press the submit button
return {
type: "SUBMIT_FORM",
names
}
};
dispatch(submitForm(names));
Besides that, according to react-redux.js.org/mapdispatch to use bindActionCreators in mapDispatchToProps, mapDispatchToProps does not return an object, but does return bindActionCreators.
const mapDispatchToProps = dispatch =>
bindActionCreators(formActions, dispatch);
another point, if you need to pass an argument to your action creator I would do like this:
const mapDispatchToProps = dispatch => ({
submitForm: (names) => dispatch(submitForm(names))
});
Unfortunately, I can't see all of your code. I hope I helped you with something.

Related

Redux | Why this store's parameter type changes after second click?

I'm trying to push a new value in the store's state. It works fine the first time I click on the button "Add item", but the second time I got the following error: "state.basket.push is not a function". I configure the action to console log the state and got the following results:
1st click: {...}{basketItems: Array [ "44" ]}
2nd click: Object {basketItems: 0 }
Why the variable type is changing from array to an int?
Here is the code for the rendered component:
function Counter({ basketItems,additem }) {
return (
<div>
<button onClick={additem}>Add item</button>
</div>
);
}
const mapStateToProps = state => ({
basketItems: state.counterReducer.basketItems,
});
const mapDispatchToProps = dispatch => {
return {
additem: ()=>dispatch({type: actionType.ADDITEM, itemName:'Dummy text' }),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
And the reducer looks like this:
import {ADDITEM} from "../actions/types";
const initialState = { basket: [], };
export default function reducer(state = initialState, action) {
switch (action.type) {
case ADDITEM:
console.log(state);
// let newBasket = state.basket.push('44');
return {
...state,
basket: state.basket.push('44')
};
default:
return state;
}
}
I'm copying the state before updating the basket to prevent weird behaviors.
There's two problems here:
state.basket.push() mutates the existing state.basket array, which is not allowed in Redux
It also returns the new size of the array, not an actual array
So, you're not doing a correct immutable update, and you're returning a value that is not an array.
A correct immutable update here would look like:
return {
...state,
basket: state.basket.concat("44")
}
Having said that, you should really be using our official Redux Toolkit package, which will let you drastically simplify your reducer logic and catch mistakes like this.

Redux state not updating with javascript object

I have this container and component and the object yeast that Im trying to put in my store. However, its not working whenever I try and save it. The object looks like this
{ yeastType : value, temp: value}
Container.js
const mapDispatchToProps = (dispatch) => {
return {
handleYeastChange: (yeast) => {
dispatch(actions.updateYeast(yeast))
}
}
};
const RecipeYeastContainer = connect(
mapStateToProps,
mapDispatchToProps
)(RecipeYeast);
Component.js
updateYeastState = (updatedYeast) => {
this.props.handleYeastChange(updatedYeast)
};
I have no errors in the console or anything. When I open my redux dev tools its telling me the state has already been updated by the time the action is called. And thus only ever saving the first letter thats input into my field. It never persists it. Its really weird. Its also never showing up in the UI. I can type as much as I want and see the action firing and the state keeping the first letter but its not showing up in the input.
Whats weird is that when I change the code to pass in both yeastType and temp to the property function and construct the object in there it works. (See below)
This works: Container.js
const mapDispatchToProps = (dispatch) => {
return {
handleYeastChange: (yeastType, temp) => {
const yeast = {yeastType, temp}
dispatch(actions.updateYeast(yeast))
}
}
};
Component.js
updateYeastState = (updatedYeast) => {
this.props.handleYeastChange(updatedYeast.yeastType, updatedYeast.temp)
};
I cannot figure out why this is happening. I thought I could just pass the object all the way through and not have to reconstruct it.
Do you dispatch your action correctly? And in using redux, you are not updating the state of the component, you're updating the store and then the value in component is from your mapStateToProps function that get from the store. Say it you're updating your store with the object named yourReducer store. ex:
Container.js:
const mapStateToProps = (state) => ({
inputValue: state.yourReducer.value
})
const mapDispatchToProps = (dispatch) => ({
inputHandler: (e) => {
dispatch(yourAction({type: 'CHANGE_INPUT', value: e.target.value}))
// The store catches the action and pass to the reducer that can read the action's type
// in `yourReducer` will catch the 'CHANGE_INPUT' type and
// return a new value as same as the value that passed from the action
}
})
export default connect(mapStateToProps)(Component)
Component.js
export default class Component extends React.Component {
{ your custom function here ... }
render() {
const { inputValue, inputHandler } = this.props
return (
<div>
{/* The input will be the same as the inputValue changed */}
<input value={inputValue} onChange={inputHandler} />
</div>
)
}
}
For debugging redux you can try this redux-devtool.

Where to dispatch multiple actions in redux?

I am using redux with connect and redux-thunk middleware and containers.
Currently when a user perform an action, example one click on a button, I need to dispatch that action (sync) which will dispatch other few actions (asynch).
I am aware dispatching actions from within the reducer is an anti pattern.
I would like to know what is a suitable place for this code.
Currently I am not sure if it should stay in:
The action creator.
In the container using store.subscribe.
The recommended way as per the documentation is in the action creator, like so:
function actionCreator(payload) {
return dispatch => {
dispatch(action1(payload))
dispatch(action2(payload))
}
}
Then you would probably want to attach the action creators as prop and pass it down to the container using mapDispatchToProps like in the example mentioned here. So it would look something like so:
const mapDispatchToProps = dispatch => ({
action1: some_payload => dispatch(action1(some_payload))
action2: some_payload => dispatch(action2(some_payload))
})
// your component
export default connect(mapStateToProps, mapDispatchToProps)(YourApp)
As other pointed out The action creator is the right place for dispatching multiple actions.
Below an example of how action1 could dispatch other actions in your action creator.
const action1 = id => {
return dispatch => {
dispatch(action2(id))
dispatch(action3(id))
}
}
The action creator is the correct location for dispatching multiple actions. Although code like the following would work:
function actionCreator(payload) {
return dispatch => {
dispatch(action1(payload))
dispatch(action2(payload))
}
}
I would highly recommend redux-thunk based action creators to always return a resolved Promise, so that such action creators can be part of another async call. So, the simplest update to the above would be:
function actionCreator(payload) {
return dispatch => {
dispatch(action1(payload));
dispatch(action2(payload));
return Promise.resolve();
}
}
It would then be possible to dispatch to the above with:
actionCreator(payload).then(doAnotherAction(anotherPayload))
or the following, if we need to maintain order of calls:
actionCreator(payload).then(() => doAnotherAction(anotherPayload))
If you wish to 'future-proof' your action creator, so that it could handle calling both async and sync action creators, you could write it as:
function actionCreator(payload) {
return dispatch =>
Promise.resolve(dispatch(action1(payload))).then(
() => dispatch(action2(payload)));
}
And, if you like ES6 arrow notation the above can be defined as:
const actionCreator = payload => dispatch =>
Promise.resolve(dispatch(action1(payload))).then(
() => dispatch(action2(payload)));
If you have a Promise Middleware, you can use this syntax so you're able to use .then() on your dispatch(topLevelAction()):
export const topLevelAction = () => dispatch => {
return Promise.all([dispatch(action1()), dispatch(action2()), dispatch(action3())])
}
While solution by #GibboK did not work for me:
const mapDispatchToProps = (dispatch) => ({
action2: id => dispatch(Actions.action2(id)),
action3: id => dispatch(Actions.action3(id)),
action1: (dateId, attrId) => {
return dispatch => {
dispatch(Actions.action2(dateId));
dispatch(Actions.action3(attrId));
}
}
});
I eventually went with redux-batched-actions. Worked like charm:
const mapDispatchToProps = (dispatch) => ({
action2: id => dispatch(Actions.action2(id)),
action3: id => dispatch(Actions.action3(id)),
action1: (dateId, attrId) =>
dispatch(batchActions([
Actions.action2(dateId),
Actions.action3(attrId)
]))
});
"had similar issue. had to create a function
that accepts object with the actions you want to
dispatch to the store and individual params for
respective action"
dispatchMultiple({params: {
params1: "<arg for first action>" ,
params2: "<arg for second action>",
},
})
const dispatchMultiple = (obj) => {
dispatch(obj.actions.action1(obj.params.params1));
dispatch(obj.actions.action2(obj.params.params2));
};
This is what worked for me for synchronous multiple actions:
// utils file
const multipleActions = (dispatch) => {
dispatch(action1())
dispatch(action2())
dispatch(action3())
}
const mapDispatchToProps = (dispatch) => {
onClickReturn: () => {
multipleActions(dispatch)
}
};
For guys in 2020...
The actions are Supposed to be made in the action Creater. For those who would like to dispatch an action and fetch/post some data from the API can use this Idea.
lets assume we have an actions.js file and we want to dispatch a loading action before fetch data.
function requestPosts() {
return {
type: "loading"
}
}
This is the fetching action function
function fetchPosts() {
return dispatch => {
// dispatch the loading
dispatch(requestPosts());
// fetch data from api
return fetch("https://www.yoururl.com/api")
.then(response => response.json())
.then(json => dispatch({
type: "fetching successful",
payload: json
}));
}
}
I don't know the exact use case but since redux uses asynchronous logic, any solution that runs the second dispatch in the next tick of the event loop should work.
store.dispatch({ type: 'ADD_TODO', text: 'Buy milk.' });
setTimeout(() => {
store.dispatch({ type: 'ADD_TODO', text: 'Take out garbage.' });
}, 0);
Promise.resolve(() => {
store.dispatch({ type: 'ADD_TODO', text: 'Water plants.' });
});
If the second dispatch depends on the actions of first dispatch, you can get the state from the store, check if it satisfies the condition then dispatch the second action. It is best to keep the action's logic clean and granular.
So, to answer the question, the right place to dispatch multiple actions is inside the click handler where the first action originates.
The easiest way is to use a specialized middleware redux-soldier:
import { createStore, applyMiddleware } from 'redux'
import { reduxSoldierMiddleware } from 'redux-soldier'
const store = createStore(rootReducer, applyMiddleware(reduxSoldierMiddleware))
store.dispatch([
{type: 'INCREMENT'}, // traditional action
addTodo('Start using redux-soldier'), // action creator
fetchUser(), // thunk action
])
redux-soldier is also a full replacement for redux-thunk
For more info check the documentation redux-soldier.

Good pattern for redux action in callback

Supposing I have an update comment action. When a user updates comment after getting a successful result from Promise I should close comment editor. This is my sample code from my project:
export const updateComment = (comment,callBack/* ? */) => {
return (dispatch, getState){
api.updateComment({...comment}).then((result) => {
/* Do something */
callback() /* ? */
})
}
}
In react component I use action like the following code:
handleUpdateComment(){
dispatch(actions.updateComment(this.state.comment,this.closeCommentEitor)
}
It works well but I think is not a good pattern to close comment editor. I'm looking a correct pattern to close editor without passing callBack like I did if any.
When you are using redux-thunk, you can dispatch an action from another action.
What you can do is that, commentEditor have a state which you store in redux and based on that state open or close the commentEditor
export const updateComment = (comment, comment_id) => {
return (dispatch, getState){
api.updateComment({...comment}).then((result) => {
/* Do something */
dispatch({type: 'CLOSE_COMMENT_EDITOR', id: comment_id})
})
}
}
After this in a reducer on this action change the state of redux store, something like
import update from 'immutability-helper'
var initialState = [{commentId: '1', commentEditorOpenStatus: false}, {commentId: '2', commentEditorOpenStatus: false}]
const reducer = (state = initialState, action) => {
switch(action.type) {
'CLOSE_COMMENT_EDITOR':
const idx = state.findIndex(obj => obj.commentId == action.id);
return update(state, {
[idx]: {
commentEditorOpenStatus: {
$set: false
}
}
})
// Other action handlers here
default: return state
}
}
The only thing that updates your application's state is your reducers.
The reducer should be responsible to update the state of your application and not your action (you are now passing getState).
I suggest you to look at redux-promise-middleware
The middleware enables optimistic updates and dispatches pending, fulfilled and rejected actions, which can be intercepted by the reducer.

Call an action inside the redux reducer

The code in my reducer is as follows:
import {ADD_FILTER, REMOVE_FILTER} from "../../../../actions/types";
const removeFilter = (state, name) => {
return state.filter(f => f.name !== name);
};
export default function addRemoveFilterReducer(state = [], action) {
switch (action.type) {
case ADD_FILTER:
let nState = state;
if(state.some(i => i.name === action.searchFilter.name))
nState = removeFilter(state, action.searchFilter.name);
return [...nState, {name: action.searchFilter.name, values: [action.searchFilter.values]}];
//Call another action
case REMOVE_FILTER:
return removeFilter(state, action.searchFilter.name);
default:
return state;
}
}
I have one component showroom and inside the showroom I have Search component and Content component.
Inside search component I handle filtering and I dispatch an action which is handled with this reducer.
After the filter is added I want to dispatch an action with all filters. How can I do that?
That action would be handled with an reducer where I would return only those cars that match search criteria and display them in the content component.
I hope you understand what I wanted to say.
Is this approach good at all?
You may consider to use redux-thunk for this.
You'll have two separate actions, one for adding filter and the other one for making search. Your addFilterAndMakeSearch thunk will be responsible for calling these actions in order. By this way, you won't be need to dispatch an action from your reducer.
// Thunk
function addFilterAndMakeSearch(searchFilter) {
return dispatch => {
dispatch(addFilter(searchFilter);
dispatch(makeSearch());
}
}
// Action Creator One
function addFilter(searchFilter) {
return {
type: 'ADD_FILTER',
payload: searchFilter,
};
}
// Action Creator Two
function makeSearch() {
return {
type: 'MAKE_SEARCH',
};
}
In order to make this work, you need to use addFilterAndMakeSearch as your onSubmit handler.
Calling an action is most probably side effect operation. As reducers should follow functional programming principles, I would argue it shouldn't trigger any actions.
To solve your use case, you can still fire two actions from place that triggered change in your reducer. We are doing that sometimes in our app. For example your component can trigger two actions or action can fire two Redux store updates.

Categories

Resources