For a project i need to implement some kind of offline redux-action queue.
And i'm using redux-thunk for async actions. But i saw that libraries like redux-offline can't handle async actions with redux thunk yet.
Does anyone have an idea how i can best tackle this?
My actions look like this:
export function fetchIdeasForOrganisationAction(organisation) {
const options = {
credentials: 'include',
};
return async (dispatch) => {
try {
const response = await fetch(`http://${SERVER_URL}:${SERVER_PORT}/organisations/${organisation}/shared/ideas`, options);
if (!response.ok) throw Error();
const ideas = await response.json();
dispatch(newIdeasFetched(ideas));
} catch (e) {
dispatch(noIdeasFoundAction('No ideas found '));
}
};
}
function newIdeasFetched(ideas) {
return { type: "NEW_IDEAS_FETCHED", value: ideas };
}
function noIdeasFoundAction(errorMessage) {
return { type: 'NO_IDEAS_FOUND', errorMessage };
}
Related
I have been using Redux-Toolkit more than the React-Redux. I came across situations where I had to make GET requests, So I recently started using Redux-Thunk (before this I used useEffect but, as it's not a standard way to handle async functions, when using redux. I learned about middleware).
Here is the code of my Thunk function nad extraReducer which handles the GET request
export const fetchData = createAsyncThunk("type/getData", async () => {
try {
const response = await axios({url});
return response.data;
} catch (error) {
console.log(error.response);
}
});
export const extraReducers = {
[fetchData.pending]: (state) => {
state.loading = true;
},
[fetchData.fulfilled]: (state, action) => {
state.loading = false;
state.products = action.payload;
},
[fetchData.rejected]: (state) => {
state.loading = false;
state.error = true;
},
};
In fetchData function my returned response.data is being used in extraReducers as payload so I can set the state easily. But, now the scenario is I have make a post request and I don't know how will I send the data to my Thunk function.
First you create the action of posting data and send the data:
export const postData = createAsyncThunk(
"type/postData",
async (data) => {
try {
const response = await axios.post("https://reqres.in/api/users", data);
// If you want to get something back
return response.data;
} catch (err) {
console.error(err)
}
}
);
Then in a place where you want to send that data you just dispatch this action with the data argument that you want to send:
const handlePost = () => {
// whatever you want to send
const data = .........
dispatch(postData(data));
}
If you want, you can also modify your extraReducers for this action.
i want to know if there is some clean code or update to make it on my code, because i think i repeat the same code on every actions on my redux, my question is how can I avoid calling axios on my actions files ?
Please take a look on my code here :
export const SignInType = (host, lang) => async (dispatch) => {
try {
dispatch({
type: USER_LOGIN_SIGNINTYPE_REQUEST,
});
const { data } = await axios.get(
`/${lang}/data?host=${host}`
);
console.log({ data });
dispatch({
type: USER_LOGIN_SIGNINTYPE_SUCCESS,
payload: data,
});
dispatch({
type: USER_LOGIN_CLEAR_ERROR,
});
} catch (err) {
dispatch({
type: USER_LOGIN_SIGNINTYPE_FAIL,
payload: err,
});
}
};
I Really want to delete the Axios name from my actions file and make it on a separate file, but how can i do this ?
Thank you
We can suggest but there's no correct answer to this, initially any redundant lines of code can be abstracted, so in order to make things a little bit easier, we need to abstract the obvious and add the meaningful, e.g:
abstract the way you write action creators:
const actionComposer = (options) => (...args) => async dispatch => {
const modifiedDispatch = (type, payload) => dispatch({ type, payload });
const { action, onSuccess, onFailed } = options(modifiedDispatch);
try {
if (action) {
const res = await action(...args)
onSuccess(res);
}
} catch (err) {
onFailed(err)
}
}
then your code can look like this:
export const SignInType = actionComposer((dispatch)=> {
return {
action: async (host, lang) => {
dispatch(USER_LOGIN_SIGNINTYPE_REQUEST);
const { data } = await axios.get(`/${lang}/data?host=${host}`);
return data;
},
onSuccess: (res) => {
dispatch(USER_LOGIN_SIGNINTYPE_SUCCESS, data);
dispatch(USER_LOGIN_CLEAR_ERROR);
},
onFailed: (err) => {
dispatch(USER_LOGIN_CLEAR_ERROR, err.message)
}
}
})
Redux Toolkit already has a createAsyncThunk API that does all the work of defining the action types and dispatching them for you. You should use that.
Alternately, you can use our RTK Query data fetching and caching library, which will eliminate the need to write any data fetching logic yourself.
I am trying to dispatch a post of an object data using async redux axios middleware. I am getting 400 https response prob due to formatting. Here is what I have for my code. Any feedback is great!
import { createAsyncThunk } from '#reduxjs/toolkit';
import axios from 'axios';
export const postResults = createAsyncThunk(
'results/postResults',
async ({ data: TestData[] }) => {
const response = await axios.post(`${url}/data/results`, { data });
return response.data;
}
);
const sendData = async (data) => {
try {
data = {
result: 'passed',
data_id: [0]
};
await dispatch(postResults(data));
} catch (err) {
console.log(err);
}
};
I am using sendData(data) in another function to trigger the event.
const processData = (e) => {
//.....
sendData(data);
}
The output I am getting when I debug under xhr.js request.send(requestData):
requestData: "{\"data\":{\"result\":\"passed\",\"data_id\":[0]}}"
I think the formatting is an issue which I have tried to remove data so I can be read and sent its request as
requestData: "{\"result\":\"passed\",\"data_id\":[0]}"
Any thoughts or feedback!
figured out, I removed the { } so its like this:
const response = await axios.post(${url}/data/results, data);
I'm trying to call an existing function that makes an API request using axios in another function which also makes an API request but its unable to execute the function called. I'm relatively new to axios and react so I'm not sure if I'm missing something here and your help would be much appreciated. Also please note, I can call the API again via axios in the second function but instead of repeating the code I would rather like to do function call.
Function 1:
export const getUsers = () => async (dispatch) => {
try {
const resp = await axiosInstance.get("/api/users/");
dispatch({ type: GET_USERS, payload: resp.data });
} catch (error) {
dispatch(returnErrors(error.response.data, error.response.status));
}
}
Function 2:
export const deactivateUser = (userID) => async (dispatch) => {
try {
const res = await axiosInstance.put(`/api/user/disable/${userID}/`);
dispatch(createMessage({ deactivateUser: res.data }));
} catch (error) {
dispatch(returnErrors(error.response.data, error.response.status));
}
}
What I'm trying to achieve is following:
export const deactivateUser = (userID) => async (dispatch) => {
try {
const res = await axiosInstance.put(`/api/user/disable/${userID}/`);
dispatch(createMessage({ deactivateUser: res.data }));
getUsers(); // This is not getting called....
} catch (error) {
dispatch(returnErrors(error.response.data, error.response.status));
}
}
Any advice or help would be much appreciated!
The await does not seem to work with Redux saga. I need to wait for my API call to finish and then execute the remaining code. What happens now is that AFTER CALL gets printed before the RESPONSE which means await does not seem to work at all. I'm using async calls but not sure what needs to be done extra from the redux saga side?
async componentWillMount() {
console.log("BEFORE CALL")
await this.props.getUserCredit()
console.log("AFTER CALL")
}
mapDispatchToProps = (dispatch) => {
return {
getUserCredit: () => dispatch(getUserCredit()),
}
};
connect(null, mapDispatchToProps)(MyComponent);
Action
export const getUserCredit = () => {
return {
type: GET_USER_CREDIT,
};
};
Redux Saga
const getUserCreditRequest = async () => {
const response = await Api.get(getCreditUrl)
console.log("REPONSE!!!")
console.log(response)
return response
}
function* getUserCredits() {
try {
const response = yield call(getUserCreditRequest);
if (response.status === okStatus) {
yield put({
userCredit: response.data.userCredit
}
));
}
} catch (error) {}
}
export function* getUserCredit() {
yield takeLatest(GET_USER_CREDIT, getUserCredits);
}
export default function* rootSaga() {
yield all([fork(getUserCredit)]);
}
Normally, init / fetching takes place during componentDidMount and don't use async or await inside components. Let the saga middleware do its thing via yield.
// In your component
componentDidMount() { // not async
this.props.getUserCredit(); // dispatch `GET_USER_CREDIT` action
}
mapDispatchToProps = (dispatch) => {
return {
getUserCredit: () => dispatch(getUserCredit()),
}
};
connect(null, mapDispatchToProps)(YourComponent);
You shouldn't be using async/await pattern. As redux-saga handles it by the yield keyword. By the time call is resolved you will have the value available in response.
in actions.js, you should have an action that will carry your data to your reducer:
export function getUserCredits(userCredit) {
return {
type: types.GET_USER_CREDIT_SUCCESS,
payload: userCredit
};
}
Your saga should handle the API call like so:
function* getUserCredits() {
try {
const response = yield axios.get(getCreditUrl); <--- This should work
// No need for if here, the saga will catch an error if the previous call failed
yield put(actions.getUserCredits(response.data.userCredit));
} catch (error) {
console.log(error);
}
}
EDIT: example of using axios with redux-saga