First, I want to call the action 'CLICK_SEARCH' to get the response from the server
then pass the response to 'CLICK_SEARCH2' to update the store.
But its failure, the console result showing 'CLICK_SEARCH' instead of 'CLICK_SEARCH2' and action.payload is undefined. Anyone help? thx
function reducreForSeach(state = initialState3, action) {
if (typeof state === 'undefined') {
return 0
}
switch(action.type) {
case 'CLICK_SEARCH': {
axios.post('abc/url', {
responseType: 'document',
headers: { 'X-Requested-With': 'XMLHttpRequest'},})
.then(function(response) {
dispatch({
type: 'CLICK_SEARCH2',
payload: response.data});
}
case 'CLICK_SEARCH2': {
console.log(action.type);
console.log(action.payload);
}
}
Have you tried declaring constants for action types? Simple as this one:
const CLICK_SEARCH = 'CLICK_SEARCH';
const CLICK_SEARCH2 = 'CLICK_SEARCH2';
function reducreForSeach(state = initialState3, action) {
//...other parts of the code
switch(action.type) {
case CLICK_SEARCH: {
//...
dispatch({ type: CLICK_SEARCH2,
//...
case CLICK_SEARCH2: {
//...
dispatch is never defined or fed to the function reducreForSeach, so it will always fail to call CLICK_SEARCH2.
Chaining actions are not directly supported by Redux. You can implement by yourself, but I suggest you to look into popular libraries like redux-thunk or redux-saga to learn how to call actions based on other action's result. They are all well-documented and recommended in Redux's official website
You don't want to do such things in reducers as those are meant to be pure functions.
You can dispatch other actions from within action generators by using redux-thunk which enables you to return a function from your action generators.
The function you return will be called with dispatch as its props and allows you to further dispatch actions, from within the callback.
e.g.
export const mySearchActionGenerator = keyword => dispatch => {
dispatch ({
type: actionTypes.MY_SEARCH_ACTION
keyword
});
dispatch ({
type: actionTypes.MY_SEARCH_ACTION
keyword
});
}
Another option would be using redux-saga which allows far more complex setups.
Related
I'm developing a small game. And so i used the redux-toolkit for the first time. (i'm used to redux)
this createSlice method is awesome but i cannot figure out how to call actions inside actions.
so the actions are autogenerated and therefore not available before generation.
example:
const gameSlice = createSlice({
name: 'game',
initialState,
reducers: {
startNewGame: (state) => {
state.activeTeam = state.config.startTeam = random(0, 1)
state.winner = undefined
},
endRound: (state, action, dispatch) => { // last param not available
// ... code that handles the rounds inputs and counts points for each team ...
if(state.red >= 30) {
// HOW?! Here's something i tried
endGame(state, Team.Red)
dispatch({type: endGame, payload: Team.Red})
dispatch({type: gameSlice.endGame, payload: Team.Red})
}
if(state.blue >= 30) {
// HOW?! Here's something i tried
endGame(state, Team.Blue)
dispatch({type: endGame, payload: Team.Blue})
dispatch({type: gameSlice.endGame, payload: Team.Blue})
}
},
endGame: (state, action) => {
state.winner = action.payload
}
}
})
This is just a small example. There is nothing async and no backend-call. I just want to nest action-calls. Simple as hell is suppose... but i have no idea how to do it in a KISS-way.
I read something with extraReducers and createAsyncThunk but i won't do this for every action as i have no async stuff in here and because then there is no benefit in using redux-toolkit anymore. It's just a hell of more code for nothing.
I think i'm blind or stupid or just confused... but this drives me crazy, right now.
Without redux-toolkit this was easy.
Any help is appreciated.
You can never dispatch more actions inside of a Redux reducer, ever.
However, you can write additional helper functions to do common update tasks in reducers, and call those functions as needed. This can be especially helpful when using Immer-powered reducers in RTK's createSlice.
Additionally, you can call other case reducers as needed, by referring to the slice.caseReducers object like:
reducers: {
endRound(state, action) {
if (someCondition) {
// "Mutate" state using the `endGame` case reducer
mySlice.caseReducers.endGame(state)
}
},
endGame(state, action) {}
}
This is an odd question, but I haven't been able to find an explicit answer for it.
In my project I'm using Redux with new Redux hooks, useSelector and useDispatch. The setup is as standard as it gets:
Reducer:
const reducer = (state = defaultState, action) => {
switch (action.type) {
type SOME_ACTION:
return {
...state,
someVariable: action.payload
}
default:
return state
}
Synchronous actions:
export function someAction(payload) {
return {
type: SOME_ACTION
payload: payload
}
And in the components I dispatch actions using
dispatch({type: SOME_ACTION, payload: payload}).
Recently I've noticed that dispatching works just fine even if the action creator doesn't exist: as in, if the SOME_ACTION type is listed in the reducer switch operator, but there is no function someAction(payload).
The question is: is writing the action creators redundant in this case? What are the best practices when working with Redux hooks?
You can certainly use action object literals
dispatch({ type: 'MY_SUPER_AWESOME_ACTION });
But creating action creators allow you to reuse code more efficiently.
const mySuperAwesomeAction = () => ({ type: 'MY_SUPER_AWESOME_ACTION });
And now anywhere in your app where you need to dispatch this action you import the action and use
dispatch(mySuperAwesomeAction());
As apps grow and actions are used in multiple components if you were using object literals you would need to find/replace all instances of the action literal, and hope you didn't typo any of them. With the single action creator definition you need only update it in a single location.
I am currently developing an application that is a copy of Instagram. It works with React-Redux, calls an external API via axios to fetch photos that are actually blog posts. I need to also pass the amount of likes (so 0) for each one, that I am adding in my fetchPhotos action creator, which causes my application to crash. This works fine whenever the action creator is only returning type and payload.
When I console logged the action it actually turned out that the promise is now not being resolved and thus followed the error.
Action creator:
export function fetchPhotos() {
const response = axios.get("https://dog.ceo/api/breed/husky/images");
return {
type: FETCH_PHOTOS,
payload: response,
likes: 0
};
}
Reducer:
export default function(state = [], action) {
switch(action.type) {
case FETCH_PHOTOS:
console.log(action);
return [action.payload.data.message, action.likes];
default:
return state;
}
}
In App:
const history = createBrowserHistory();
const store = createStore(
connectRouter(history)(reducers),
compose(applyMiddleware(routerMiddleware(history), ReduxPromise))
);
Is there any way to make the action creator actually resolve this promise inside the action.payload?
For documentation:
As #fshauge mentioned, the payload has to be a promise and adding the property of likes breaks it. I found this in the issues, which has solved my issue. The likes property actually has to go into meta, so the end result that functions correctly is:
export function fetchPhotos() {
const response = axios.get("https://dog.ceo/api/breed/husky/images");
return {
type: FETCH_PHOTOS,
payload: response,
meta: {
likes: 0
}
};
}
According to the documentation of redux-promise, it expects either a promise or a Flux Standard Action (FSA) where the payload is a promise. Since adding the 'likes' property breaks FSA-compliancy, it has to be inside the payload somehow.
I suggest returning a promise directly from the action creator with the 'likes' property embedded as follows:
export async function fetchPhotos() {
const response = await axios.get("https://dog.ceo/api/breed/husky/images");
return {
type: FETCH_PHOTOS,
payload: {
data: response.data,
likes: 0
}
};
}
redux-promise will automatically call dispatch when the promise resolves, and you can use the following code in your reducer:
export default function (state = [], action) {
switch (action.type) {
case FETCH_PHOTOS:
console.log(action);
return [action.payload.data.message, action.payload.likes];
default:
return state;
}
}
I have an icon that when clicked it triggers a function that calls an API and then dispatch an action to remove a blog post from the state. But the problem is that my UI does not re-render. However, if I refresh my browser the post that I deleted is no longer there and my store matches my state.
Here is my function that calls an API and then dispatch an action:
export function deletePost(postID) {
return (dispatch) => {
fetch(`${url}/posts/${postID}`, { method: 'DELETE', headers})
.then((response) => response.json())
.then((postID) => dispatch(removePost(postID)))
.catch((error)=>{console.log('dispatch error',error)});
};
Here is my action:
export function removePost ( postID ) {
return {
type: REMOVE_POST,
postID,
}
}
And here is my reducer:
function posts (state = {}, action) {
switch (action.type) {
case REMOVE_POST:
return [
...state.filter((post)=>post.id!==action.postID)
];
default:
return state
}
}
Now when I simply dispatch an action without calling an API
export function deletePost(postID) {
return (dispatch) => {
dispatch(removePost(postID));
}
My state is correctly updated but of course my redux store is not. When I do the calling of API before dispatching an action as shown earlier, there is also no error coming from the console log.
What could be the problem here? I am very new to ReactJS and can't find a solution yet to this problem after many tries.
Also, as a note, I am using redux-thunk in this project.
I have a few questions, but first: I think the problem is here:
[
...state.filter((post)=>post.id!==action.postID)
]
What is the state's shape? Is it state = [post1, post2, ...]? I can see the initial state is {}, so I find it weird to be calling state.filter and not state.posts.filter or whatever here.
The other might be problem, is with post.id !== action.postID, maybe the received ID is an number type, and maybe the local id is a string? Maybe the other way around?
const requestUserInfo = (state = {}, action) => {
switch(action.type) {
case 'HEADER_GET_USERINFO':
axios.post('/user/getScores', qs.stringify({
token: token,
uid: uid
}))
.then(res => {
if(res.data.status !== 200) {
message.error(res.data.message)
return state
}
return { ...res.data.attachment, ...state }
})
break
default :
return state
}
}
// error
Uncaught Error: Given action "HEADER_GET_USERINFO", reducer "requestUserInfo" returned undefined. To ignore an action, you must explicitly return the previous state.
You should avoid side effects inside your reducers!
As mentioned in the official Redux tutorial:
Things you should never do inside a reducer:
Mutate its arguments
Perform side effects like API calls and routing transitions
Call non-pure functions, e.g. Date.now() or Math.random().
You should use Async actions instead, which will execute the API call, and at least possibly dispatch an action that should handle the incoming data.