Redux, best way to access and use the state inside a middleware - reactjs

I have a situation that I need to send two params in my post requests using axios to all my endpoints. These parameters are already in my store.
So, I create a "middleware" to procede all my requests like so:
const POST = async (url, body = {}) => {
const {Token, UserId} = store.getState().User
const {DeviceIdentifier} = await GetDeviceInfo()
const data = {AgentId: UserId, DeviceId: DeviceIdentifier, ...body}
const response = await axios.post(`${BASE_URL}${url}$`, data, {
headers: {Authorization: `Bearer ${Token}`},
})
if (!response.data.Logged) {
logoutUser()
return false
}
return response
}
But, I've been reading a lot of articles saying that using getState() like that is not a good idea...
What should be the best aproch in a situation like that?
Thank you so much!

When using redux thunk you can supply two arguments dispatch and getState. To supply more arguments:
const store = createStore(
reducer,
applyMiddleware(thunk.withExtraArgument({ url, body }))
)
// .withExtraArgument(url) // to supply further one arguments only
And then you can use getState like:
const POST = async (dispatch, getState, {url, body = {}}) => {
...
getState().User

Using getState() in a middleware is totally fine - that's why it's there!
If you'd like to read some extended discussion of some hypothetical concerns and my reasons why it's still okay, my post Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability goes into further details.

Related

Is it necessary to save everything in redux state?

I'm using redux-toolkit. Everything it's ok, but now I'm facing an architecture problem.
I have an endpoint that I need to call in order to get some data so I can do a final call.
This final call response will be the one that I'll use in order to create and do some logic in order to dispatch and save in the store, so:
1- Should I do both calls in the same createAsyncThunk call and just return what I need?
2- this asyncthunk call will just handle the data, i dont really need it to save anything, based on those 2 calls, it will dispatch others actions. Where I should place this?
I have async thunks that dispatch other async thunks, no problem.
1- Yes, although it might be harder to track which api failed
2- It's easier if this thunk lives with the others in my opinion
Example:
const BASE = 'user';
// some other thunk in another file, ignore implementation
const saveInLocalStorageAC = createAsyncThunk()
const fetchUserAC = createAsyncThunk(`${BASE}/fetch`, async (email, { dispatch }) => {
const id = await getIdFromEmail(email); // service
await dispatch(saveInLocalStorageAC({ loggedInUser: { id, email } })); // thunk
return id;
})
const slice = createSlice({
name: BASE,
initialState: someInitialState,
reducers: {},
extraReducers: builder => {
builder.addCase(fetchUserAC.fulfilled, (state, action) => {
// here is where I decide if I want to update the state or not
state.data = action.payload // id
})
},
})
export default slice.reducer;
If you check the actions with some sort of logger you would see something like:
user/fetch/pending
storage/save/pending
storage/save/fulfilled
user/fetch/fulfilled

How to dispatch data to redux from the common api request file?

I have created a common js file to call the service requests. I want to store the fetched data in my redux managed store. But I am getting this error saying Invalid hook call. Hooks can only be called inside of the body of a function component.I think this is because I am not using react-native boilerplate for this file. But the problem is I don't want to I just want to make service requests and responses.
import { useDispatch } from "react-redux";
import { addToken } from "../redux/actions/actions";
const { default: Axios } = require("axios");
const dispatch = useDispatch();
const handleResponse=(response, jsonResponse)=> {
// const dispatch = useDispatch(); //-----also tried using dispatch here
const jsonRes = jsonResponse;
const { status } = response;
const { errors } = Object.assign({}, jsonRes);
const resp = {
status,
body: jsonResponse,
errors,
headers: response.headers,
};
console.log(resp, 'handle response');
return await dispatch(addToken(resp.body.token))
};
const API = {
makePostRequest(token) {
Axios({
url: URL,
...req,
timeout: 30000
}).then(res =>
console.log('going to handle');
await handleResponse(res, res.data)
})
}
export default API
I know there would be some easy way around but I don't know it
Do not use useDispatch from react-redux, but dispatch from redux.
You need to use redux-thunk in your application.
Look at the example in this article Redux Thunk Explained with Examples
The article has also an example of how to use redux with asynchronous calls (axios requests in your case).
I suggest to refactored your api to differentiate two things:
fetcher - it will call your api, e.g. by axios and return data in Promise.
redux action creator (thunk, see the example in the article) - it will (optionally) dispatch REQUEST_STARTED then will call your fetcher and finally will dispatch (REQUEST_SUCCESS/REQUEST_FAILURE) actions.
The latter redux action creator you will call in your react component, where you will dispatch it (e.g. with use of useDispatch)

When to use Redux to fetch data from api call

I adopted Redux in my project for state control, and also Axios for fetching api in action.
But I wonder when should I fetch data using API call through Redux (in action), when should I directly make the api call in component.
Is it depending on, whether I need to store the response data in Redux (for sharing among different components)? May I know any best practice for it?
API call through Redux
export const fetchOptions = () => {
return async (dispatch, getState) => {
const request = await client.query({
query: gqlQueries.getQuery,
});
const options = await request;
dispatch({
type: types.FETCH_DATA_END,
options: options
});
}
}
Directly make API call in component:
const axios = require("axios");
useEffect(() => {
axios({
url: 'http://localhost/graphql',
method: 'post',
data: {
query: `
query PostsForAuthor {
author(id: 1) {
firstName
posts {
title
votes
}
}
}
`
}
}).then((result) => {
console.log(result.data)
});
}, []);
If multiple components are using the same data, redux shines there. API calls in components are preferred when you do not want any stale data to show, therefore you call api every time component mounts and your data is always in sync with your back end. There might be some other criteria but these two help me decide , where to keep the state.

What is the best approach of writing redux actions that need data from other actions

I have made some research about possible ways to do it, but I can't find one that uses the same architecture like the one in the app I'm working on. For instance, React docs say that we should have a method which makes the HTTP request and then calls actions in different points (when request starts, when response is received, etc). But we have another approach. We use an action which makes the HTTP call and then dispatches the result. To be more precise, my use case is this:
// action to get resource A
getResourceA () {
return dispatch => {
const result = await axios.get('someLink');
dispatch({
type: GET_RES_A,
payload: result
});
};
}
// another action which needs data from resource A
getSomethingElseByIdFromA (aId) {
return async dispatch => {
const result = await axiosClient.get(`someLink/${aId}`);
dispatch({
type: GET_SOMETHING_BY_ID_FROM_A,
payload: result
});
};
}
As stated, the second action needs data from the first one.
Now, I know of two ways of doing this:
return the result from the first action
getResourceA () {
return async dispatch => {
const result = await axios.get('someLink');
dispatch({
type: GET_RES_A,
payload: result
});
return result;
};
}
// and then, when using it, inside a container
async foo () {
const {
// these two props are mapped to the getResourceA and
// getSomethingElseByIdFromA actions
dispatchGetResourceA,
dispatchGetSomethingElseByIdFromA
} = this.props;
const aRes = await dispatchGetResourceA();
// now aRes contains the resource from the server, but it has not
// passed through the redux store yet. It's raw data
dispatchGetSomethingElseByIdFromA(aRes.id);
}
However, the project I'm working on right now wants the data to go through the store first - in case it must be modified - and only after that, it can be used. This brought me to the 2nd way of doing things:
make an "aggregate" service and use the getState method to access the state after the action is completed.
aggregateAction () {
return await (dispatch, getState) => {
await dispatch(getResourceA());
const { aRes } = getState();
dispatch(getSomethingElseByIdFromA(aRes.id));
};
}
And afterward simply call this action in the container.
I am wondering if the second way is all right. I feel it's not nice to have things in the redux store just for the sake of accessing them throughout actions. If that's the case, what would be a better approach for this problem?
I think having/using an Epic from redux-observable would be the best fit for your use case. It would let the actions go throughout your reducers first (unlike the mentioned above approach) before handling them in the SAME logic. Also using a stream of actions will let you manipulate the data throughout its flow and you will not have to store things unnecessary. Reactive programming and the observable pattern itself has some great advantages when it comes to async operations, a better option then redux-thunk, sagas etc imo.
I would take a look at using custom midleware (https://redux.js.org/advanced/middleware). Using middleware can make this kind of thing easier to achieve.
Something like :
import {GET_RESOURCE_A, GET_RESOURCE_B, GET_RESOURCE_A_SUCCESS, GET_RESOURCE_A_ERROR } from '../actions/actionTypes'
const actionTypes = [GET_RESOURCE_A, GET_RESOURCE_B, GET_RESOURCE_A_SUCCESS, GET_RESOURCE_A_ERROR ]
export default ({dispatch, getState}) => {
return next => action => {
if (!action.type || !actionTypes.includes(action.type)) {
return next(action)
}
if(action.type === GET_RESOURCE_A){
try{
// here you can getState() to look at current state object
// dispatch multiple actions like GET_RESOURCE_B and/ or
// GET_RESOURCE_A_SUCCESS
// make other api calls etc....
// you don't have to keep stuff in global state you don't
//want to you could have a varaiable here to do it
}
catch (e){
} dispatch({type:GET_RESOURCE_A_ERROR , payload: 'error'})
}
}
}

Redux: async actions without modifying the store

Is it a good idea to send http requests from redux-thunk middleware even if this http request doesn't modify the store?
Here is some code explaing what I mean:
export const CONFIRM_UPLOAD_REQUEST = 'CONFIRM_UPLOAD_REQUEST';
export const CONFIRM_UPLOAD_SUCCESS = 'CONFIRM_UPLOAD_SUCCESS';
export const CONFIRM_UPLOAD_FAILURE = 'CONFIRM_UPLOAD_FAILURE';
function _confirmUpload() {
return {
[CALL_API]: {
types: [CONFIRM_UPLOAD_REQUEST, CONFIRM_UPLOAD_SUCCESS, CONFIRM_UPLOAD_FAILURE],
requestMethod: 'POST',
endpoint: `upload/confirm`
}
};
}
export function confirmUpload() {
return (dispatch) => dispatch(_confirmUpload());
}
When I send this action, my middleware will execute POST upload/confirm request. It will not modify the store (so I don't have a reducer for CONFIRM_UPLOAD_SUCCESS).
The question: how bad is this approach? Must I execute http request directly (not via middleware)?
Answering on my own question, yes it's bad practice because the component will be re-rendered even if its properties were not changed

Resources