I want to append the state.data with action.payload and i have tried everything it gives unidefined on .concat, .push or spreading in an array.
import { createAction, createReducer } from "#reduxjs/toolkit";
const initialState = {};
const request = createAction("allDataRequest");
const success = createAction("allDataSuccess");
const fail = createAction("allDataFailure");
const clear = createAction("clearErrors");
export const allServicesReducer = createReducer(initialState, (builder) => {
builder
.addCase(request, (state, action) => {
state.loading = true;
})
.addCase(success, (state, action) => {
state.loading = false;
state.data = action.payload;
// I want to append this state.data with payload
//state.data = state.data.concat(action.payload)
//state.data = [...state.data, ...action.payload]
// However i get stae.data undefined on both
})
.addCase(fail, (state, action) => {
state.loading = false;
state.error = action.payload;
})
.addCase(clear, (state, action) => {
state.error = null;
});
});
This is because your initial state does not have a .data field:
const initialState = {};
So, yes, it's going to be empty to begin with.
You need to either provide const initialState = {data: []} so there is a field, or update the reducer logic to handle the case where it doesn't exist.
Related
I am fetching two APIs in parallel and I want to render the data together but I am unable to render the data from the second API.
I am attaching the expo link of the project.
https://snack.expo.dev/#keshav1973/thunk
Let minify fetchUsers function as below
const getLinksDataPromises = async ()=> {
const links = ["https://jsonplaceholder.typicode.com/users","https://jsonplaceholder.typicode.com/posts"]
return links.map( async link=> {
const response = await fetch(link)
const data = await response.json()
return data
})
}
export const fetchUsers = () => {
return async dispatch => {
dispatch(getUsersRequest());
const promises = await getLinksDataPromises()
const results = await Promise.all(promises);
const users = results[0]
const posts = results[1]
dispatch({ type: 'GET_USERS_SUCCESS', payload: users})
dispatch({ type: 'GET_POSTS_SUCCESS', payload: posts})
};
};
The issue was not copying the previous state value before you update the new value. That's why the second dispatch clear the previous dispatched value.
Updated reducer function
const initialState = {
users: [],
posts: [],
loading: false,
error: null,
};
const users = (state = initialState, action) => {
console.log({action})
switch (action.type) {
case 'GET_USERS_REQUEST':
return {...state, loading: true};
case 'GET_USERS_SUCCESS':
return {...state,loading: false, users: action.payload};
case 'GET_USERS_FAILURE':
return {...state,loading: false, error: action.payload};
case 'GET_POSTS_SUCCESS':
return {...state,loading: false, posts: action.payload,}
default:
return state;
}
};
export default users;
You can test working example here
https://snack.expo.dev/#emmbyiringiro/230e21
I have a api with rtk (redux-toolkit) with a state 'search'
const [search, setSearch] = useState({nexToken: null})
const { data, isLoading, isError } = useGetPostQuery(search ?? skipToken)
useEffect(() => {
console.log('data', data)
}, [data])
When I call methods to add parameters to my search and set the new state does not happen again the api, why?
for example:
const addCity = () => {
const newFilter = { city: 'London. }
setSearch({...search, ...newFilter})
}
I am trying to developp my 1st React Native app but I am bloqued with store/fetching data from firestore.
The problem is that is dispatch before fetching all data from firestore so my filteredProfiles array is empty and my flat list in my screen is so empty too.
what should I modify? If someone could help me, i will appreciate..Thanks!!
here is my code in my screen:
ProfilesListScreen.js
const ProfilesListScreen = props => {
const connectedUser = useSelector(state => state.profiles.user);
const filteredProfiles = useSelector(state => state.profiles.filteredProfiles)
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchProfiles());
}, [dispatch]);
My profilesAction.js in my store :
export const fetchProfiles= () => {
const loadedProfiles = [ ];
return async dispatch => {
await firebase.firestore().collection('users')
.get()
.then((profileSnapShot) => {
profileSnapShot.forEach((doc) => {
const firstname= doc.get("firstname");
const birth = doc.get("birth");
const age = getAge(birth.seconds);
const sex = doc.get("sex");
const newProfile= new profile(doc.id, firstname,age, "https://www.google.fr/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png");
loadedProfiles.push(newProfile);
});
})
dispatch({ type: SET_PROFILESLIST, profilesList: loadedProfiles } ) ;
};
and my reducer profiles.js
case SET_PROFILESLIST:
const newProfilesList = action.profilesList;
return {
filteredProfiles : newProfilesList,
};
Have a try by updating the code for fetchProfiles as below this might help you with your issue.
export const fetchProfiles= () => {
const loadedProfiles = [];
return async dispatch => {
const profileSnapShot = await firebase.firestore().collection('users').get()
profileSnapShot.forEach((doc) => {
const firstname= doc.get("firstname");
const birth = doc.get("birth");
const age = getAge(birth.seconds);
const sex = doc.get("sex");
const newProfile= new profile(doc.id, firstname,age, "https://www.google.fr/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png");
loadedProfiles.push(newProfile);
});
dispatch({ type: SET_PROFILESLIST, profilesList: loadedProfiles });
}
};
I'm currently facing a problem, when I try to reject a value when my node-fetch request fail, thunkApi.rejectWithValue() isn't working. However when my request is pending or when It's fulfilled, It's working fine.
Here's my slice :
export const userSlice = createSlice({
name: "user",
initialState: initialState as User,
reducers: {
...
},
extraReducers: (builder) => {
...
builder.addCase(changePassUser.pending, (state) => {
GGLog("FETCHING CHANGEPASS API...");
state.isFetching = true;
return state;
});
builder.addCase(changePassUser.fulfilled, (state, { payload }) => {
GGLog("FULFILLED CHANGEPASS:", JSON.stringify(payload));
state.isFetching = false;
state.isSuccess = true;
state.isError = false;
return state;
});
// eslint-disable-next-line #typescript-eslint/no-explicit-any
builder.addCase(changePassUser.rejected, (state, { payload }: any) => {
GGLog("REJECTED CHANGEPASS:", JSON.parse(payload));
state.isFetching = false;
state.isError = true;
state.errorMessage = payload.data;
return state;
});
},
});
Here's my thunk :
export const changePassUser = createAsyncThunk(
"users/password/update",
async ({ oldpassword, newpassword }: RegisterParameters, thunkAPI) => {
try {
const res = await changePassApi.changePass.return({
oldpassword: oldpassword,
newpassword: newpassword,
});
GGLog("API_CHANGEPASS_RES:", res);
const data = await res.json();
if (res.ok) {
GGLog("API_DATA_RESPONSE_OK: ", data);
const tokenData = JSON.stringify(res.headers);
const token = JSON.parse(tokenData).map["x-auth"];
await localStorage.store("token", token);
return data;
} else {
GGLog("API_DATA_RESPONSE_NOK: ", data);
return thunkAPI.rejectWithValue(data);
}
} catch (e) {
GGLog("Error while fetching Login API => ", e);
return thunkAPI.rejectWithValue(e);
}
}
);
And here's the result in the console :
Console output
Any ideas ? Am I missing something ?
Thanks :)
Okay I've found my problem, I was just focused on the thunk and didn't pay attention to the promise rejection. I was trying to parse a JSON that does'nt exist... Just remove the GGLog("REJECTED CHANGEPASS:", JSON.parse(payload));in the slice.
It's working fine now !
I want to use redux-observerable to my project,because the action of if can be canceld.But the offical gives the example which uses the ajax of rxjs, I want to use axios as the network library, how to realize it.
the example code:
const FETCH_USER = 'FETCH_USER';
const FETCH_USER_FULFILLED = 'FETCH_USER_FULFILLED';
const FETCH_USER_REJECTED = 'FETCH_USER_REJECTED';
const FETCH_USER_CANCELLED = 'FETCH_USER_CANCELLED';
const fetchUser = id => ({ type: FETCH_USER, payload: id });
const fetchUserFulfilled = payload => ({ type: FETCH_USER_FULFILLED, payload });
const cancelFetchUser = () => ({ type: FETCH_USER_CANCELLED });
const fakeAjax = url => of({
id: url.substring(url.lastIndexOf('/') + 1),
firstName: 'Bilbo',
lastName: 'Baggins'
}).pipe(delay(1000));
const fetchUserEpic = action$ => action$.pipe(
ofType(FETCH_USER),
mergeMap(action => fakeAjax(`/api/users/${action.payload}`).pipe(
map(response => fetchUserFulfilled(response)),
takeUntil(action$.pipe(
filter(action => action.type === FETCH_USER_CANCELLED)
))
))
);
const users = (state = {}, action) => {
switch (action.type) {
case FETCH_USER:
return {};
case FETCH_USER_FULFILLED:
return {
...state,
[action.payload.id]: action.payload
};
default:
return state;
}
};
const isFetchingUser = (state = false, action) => {
switch (action.type) {
case FETCH_USER:
return true;
case FETCH_USER_FULFILLED:
case FETCH_USER_CANCELLED:
return false;
default:
return state;
}
};
I want replace fetchAjax use axios
const fakeAjax = url,params =>{ return axios({
method: 'post',
url: url,
data: params
});
}
I don't understand the added value of using axios, since ajax from rxjs will simplify your code (it is already using observables). However, if you really want to it is definitely possible. I assume in the example below that you are using actions where the payload consists of a url and request data.
const fetchUserEpic = action$ => action$.pipe(
ofType(FETCH_USER),
mergeMap(action => from(axios({method: 'get', action.payload.url, data: action.payload.data})).pipe(
map(response => fetchUserFulfilled(response)),
takeUntil(action$.ofType(FETCH_USER_CANCELLED)),
))
);
Also: keep in mind that cancelling will prevent the redux store from being updated, but it will not cancel the axios request from being processed.