If an action needs to use some current state, who should fetch it from store? - reactjs

The title may not clear enough, please consider this example:
If I have a data table, which you can select multiple rows, and click action button like delete.
now in my actions.js:
(selectedRows is an array that contains the row indexes, getSelectedPostIds is a selector which will fetch and convert selectedRows to postIds)
import { getSelectedPostIds } from 'selectors'
export const deletePosts = () => (dispatch, getState) => {
// encapsulate the parameter `postIds` in action
const postIds = getSelectedPostIds(getState())
dispatch({ type: 'DELETE' })
deletePostsApi(postIds)
// .then(...)
// .catch(...)
}
is there any problem in this design? Or I should avoid using getState in an action and just pass postIds as a parameter to the action:
export const deletePosts = postIds => dispatch => {
dispatch({ type: 'DELETE' })
deletePostsApi(postIds)
// .then(...)
// .catch(...)
}
The only difference is that who should fetch the state (use the selector) from store, 1. action or 2. the component who will dispatch the action (via mapStateToProps).
I'm not sure about the approach 1, and the approach 2 will make my component contains a lot of props just because some actions need them (or maybe this is totally fine?).
thanks.

This might be a matter of taste. I usually like to access getState directly since, as you point out, avoids passing a lot of props. And by doing that the action is easier to integrate in different components (I just need to call it instead of additionally editing the mapStateToProps). Also, since in the end both ways are accessing the global store, the intended redux data flow is not compromised in any way.

You can use redux-thunk if you want to work with state in your action creators. :)
https://github.com/gaearon/redux-thunk
function yourActionCreator() {
// Redux-thunk will catch all action creators that return functions
return (dispatch, getState) => {
// u can use state here
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
// Dispatch your action creator as you would normally do
dispatch(increment());
};
}

Related

Best way to check if some data is present in the redux-store before performing an action

I have a react-redux app and one of the actions triggered requires checking the presence of some data in the store. If the data is not present I want to discard the action and don't want to proceed, but if the the data is present we want to fire another action that updates the store.
I was wondering what would be the correct way to do that? The code snippet/pseudo code below mimics something similar.
<MyComponent onClick={onClickHandler}/>
onClickHandler = () => {
if(checkIfDatapresentInStore) {
// anActionHandler();
} else {
anotherActionHandler();
}
}
//Redux-store
store = {
dataPresentInStore: true
}
Thanks
You can use getState store method. It will return current state present in the store. Then you just need to check the state you are looking for and trigger actions based on that.
To elaborate on Sunny's answer, this is something that's possible to do either within the action creator, or within the component's handler function. It's really up to you if you want to make the dataPresent state available to your component or not.
Via action creator in actions.js:
// Note: the second argument to action callback is
// a function that returns the whole store
const conditionalAction = () => (dispatch, getState) => {
// Retrieve the whole store object and check what you need from it
const { dataPresent } = getState();
// Conditionally dispatch an action
if (dataPresent) {
dispatch({ type: "MAIN_ACTION" });
} else {
dispatch({ type: "OTHER_ACTION" });
}
}
OR via your example in MyComponent.js.

Redux action dispatch

In my redux action, I have one action will be called by another two actions, code is below:
export const addParticipantFromPopupRequest = (participant, project_id, currentStep) => async (dispatch) => {
const result = await addParticipant(participant)
dispatch({ type: PARTICIPANT_ADD, payload: result })
dispatch(updateProjectStep(project_id, currentStep))
}
export const handleFinalStep = (projectId, currentStep) => async (dispatch) => {
dispatch(updateProjectStep(projectId, currentStep))
}
const updateProjectStep = (projectId, currentStep) => async (dispatch, getState) => {
dispatch({ type: MODAL_STATUS_CHANGE, payload: { projectId, currentStep } })
dispatch({ type: PROJECT_PROCESS_LIST_UPDATE, payload: { project_id: projectId, currentStep } })
const { projectsProcessListsReducer } = getState()
localStorage.setItem("projectsProcessLists", JSON.stringify(projectsProcessListsReducer))
}
If I dont' use dispatch when call updateProjectStep, the addParticipantFromPopupRequest and handleFinalStep cannot run correct.
My question is can I call dispatch actions in this way and is it correct? Why I need the "dispatch" when I call updateProjectStep in another actions rather than call function name directly?
My question is can I call dispatch actions in this way and is it correct?
Yes. You should always call with the dispatch.
Why I need the "dispatch" when I call updateProjectStep in another actions rather than call function name directly?
If you call updateProjectStep directly without dispatch, it will become a normal js function call and your store won't be aware of it. Dispatch is the only way to trigger a state change in store.
In redux the store is single source of truth, the dispatch you are using is actually comes from store (store.dispatch).
If you call a function normally then it won't be aware by the store. That action won't pass through the middlewares (thunk/saga) that store is aware of and won't do the store update via reducers.
If store is not updated, your components won't receive any updates. Eventually your UI won't re-render.
You can find more about dispatch here.

How to chain redux actions using returned result of the previous action?

I'm building an app in React Native, and using Redux with redux-persist to act as on device database.
The crux of the issue is, how do I return the result of a redux action, to then dispatch another action with this data? Read on for more specifics.
The user can create custom habit types. When doing so, I dispatch an action to create a habit type in the store (e.g. "running"). This action generates a new unique UUID for the habit type. I then want to add this newly created habit type to a routine (e.g. "morning routine"), and so I need to receive back the UUID of the habit type and call another dispatch to add it to the routine.
I'm using immer to make manipulating the state in my reducers simpler, and have this code (simplified example):
import produce from "immer";
const userReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_CUSTOM_HABIT_TYPE: {
return produce(state, draftState => {
const newHabitType = {
id: generateUuid(),
name,
};
draftState.customHabitTypes.push(newHabitType);
return draftState;
});
}
}
};
I'm then dispatching it in my component, like so (simplified):
dispatch({
type: ADD_CUSTOM_HABIT_TYPE,
name: "running",
});
How can I then say, after creating this new habit type, to dispatch another action and add it to my routine?
I've looked at redux-thunk and redux-saga, and spent hours reading about these and trying to get redux-thunk to work, but to no avail. I'm sure this must be simple, but I'm coming up blank, and so maybe others are too, hence this post.
A very simple solution would be to generate the unique id before dispatching the action.
Example
const newHabitType = {
id: generateUuid(),
name,
};
dispatch({
type: ADD_CUSTOM_HABIT_TYPE,
habit: newHabitType,
});
dispatch({
type: ADD_CUSTOM_HABIT_TO_ROUTINE,
habit: newHabitType.id,
});
Pros
You no longer need to chain actions per se, you just need to dispatch them in order.
This preserves one of the most important Redux guidelines: your reducer should not have any side effects (in your case, generating a random id). reference
Cons
If you create the new habits in multiple places, you will have to generate the unique ids in every place where you dispatch the action. This might lead to repeated code. The solution to this would be to encapsulate the whole logic for creating the habits to a single component and then reuse this component everywhere.
Actions do not return data per se, the are simply objects which mutate the store based on the rules defined in the reducer. Two possible solutions:
Option A, create a composite action.
const compositeAction = args => {
return dispatch => {
return someAsyncCall(args).then(response => {
dispatch(addCustomHabitat(response))
dispatch(followUpAction())
}
}
}
const addCustomHabitat = response => {
return {
type: "ADD_CUSTOM_HABIT_TYPE",
data: response
}
}
const followUpAction = () => {
...another action...
}
Option B, connect the results of the first action to the dispatching component through react-redux and pass them to the second action.
import {connect} from 'react-redux';
const MyReactComponent = props => {
dispatch(addCustomHabitatTypeAction());
if(props.customHabitatType !== undefined)
dispatch(followUpAction(props.customHabitatType());
return (
...JSX here...
);
}
const mapStateToProps = state => {
return {
customHabitatType: state.userReducer.customHabitatType
}
}
connect(mapStateToProps)(MyReactComponent);
I hope this helps! Please excuse my abbreviated code and let me know if you have any questions.

How can I dispatch a change in the constructor?

I am trying to learn redux. I think I have reducers down pretty well, I can pull the data from the store and set it via props.
But I cannot wrap my head around actions and changing data on the state of the store.
I have this:
const mapDispatchToProps = (dispatch) => {
return{
what goes in here?
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ComponentName);
I need to know how to dispatch a change inside the const. just a simple add text to an empty state. example: the state is apples:'', and I want to add 'red delicious' to that.
mapDispatchToProps provides you a way to connect your action creators to your components. Let's assume you have an action creator which increments a counter state
export const change = value =>({
type: 'CHANGE_FRUIT',
fruit : value
})
And you want value to be passed from one of your components. First use connect HOC in this component like you're already doing. Now you need to import incrementCounter from your actions folder
import { change as changeFruit } from './actions/fruit'
Now use mapDispatchToProps like this
const mapDispatchToProps = dispatch =>({
change : fruit => dispatch(changeFruit(fruit))
})
Now you have an action creator serialized inside your component's props and when you call props.increment(2) this will be the equivalent to call
dispatch(changeFruit('apple'))
Here is why you should always do props.increment instead of directly call dispatch inside your component.
So the full implementation inside your component could be like this
import { change as changeFruit } from './actions/fruit'
class Component extends React.component{
render(){
const { fruit, change } = this.props
return(
<>
<div>{fruit}</div>
<button onClick={() => change('orange')}>Change to orange</button>
</>
)
}
}
const mapStateToProps = state =>({
fruit : state.fruit
})
const mapDispatchToProps = dispatch =>({
change : fruit => dispatch(changeFruit(fruit))
})
export default connect(mapStateToProps, mapDispatchToProps)(Component)
Your reducer should look like this
const initialState = {
fruit: 'apple'
}
export const reducer = (state = initialState, action) =>{
switch(action.type){
case 'CHANGE_FRUIT' : return{
...state,
fruit : action.fruit
}
default : return state
}
}
An "action" is just a plain object that describes some sort of change you want to happen in your store. So in your case you want to add text to your string, such an action might look like:
{
type: 'ADD_TEXT',
value: 'red delicious'
}
An action creator is nothing more than a function that returns such a plain object. Notice we can generalise the action creator, so you can pass in the string to add, rather than have it hard coded as 'red delicious'.
const addText = value => ({ type: 'ADD_TEXT', value })
In order to 'send' this action to the reducers, it needs to be passed to dispatch function that Redux provides. E.g. this would dispatch the action:
const action = addText('red delicious') // Create the plain action object
dispatch(action) // Send it to the reducers
That can get kind of tedious to write out manually all the time, so mapDispatchToProps helps with that. It's a function you provide to connect that does the above all in one place, leaving the rest of your component code un-cluttered. Redux calls it internally to generate the props for your connected component. So when you do,
const mapDispatchToProps = dispatch => ({
addText: value => {
// Same code as above:
const action = addText('red delicious')
dispatch(action)
}
})
In your component you can call
props.addText('red delicious')
look when use the connect function in the app it takes 2 arguments the first is the state current and the second argument is the dispatch that specifies what type of action that will be implemented is the specific action the dispatch depended on the despatch will be called the specific action that link by reducer that implements in provider in-store when use
<Provider store={store}>
which the reducer that created by the const store=createStore(rootReducer)
const mapDispatchToProps = (dispatch) => {
return{
example look this is dispatch that well happened it will call specif action bu use key type
this == store.dispatch(constants.WIDITHROUP)
the first is key or name of the handle when you want to implement :(input that enters fro this dispatch and=> it call store.dispatch(name of Handel) )
widthroup: (balance) => dispatch({ type: constants.WIDITHROUP ,balance}),
deposit: (balance) => dispatch({ type: constants.DEPOSIT,balance }) }
}
}
Basically I am answering for the part where you have confusion:
return {
what goes in here?
}
Ideally, if you think as class concept, let's say you have a class that facilitates to choose and save your favorite color selection.
So, all you want is to create a class component and define a method saveSelection that should save selection to store. And, who ever wants to get that selection or see your selected colors they can access from store.
Back to Redux, the rule says, you can not update the state of store directly. You have to dispatch an action.
So, if I already have a decided to create a method saveSelection for that purpose, what should I do, so that it should dispatch an action.
May be something like:
class ColorSelector {
saveSelection () {
dispatch(action); //dispatch is some way to dispatch an action
}
}
But, action is not just a command or text or a value, we need to specify, the type of action (because reducer need to identify which data it need to update based on action) and the data I want to save. That will make sense of an action.
So, I want something like:
//I pass my selected color to saveSelection method and that should save
saveSelection(dataToUpdate) {
dispatch({type: actionType, data: dataToUpdate})
}
One thing may be going inside your head -> How will I get dispatch and how did I just use it here?
That will get clear in a sec, but first lets replace the dispatch arguments which we can write like:
saveSelectionAction(dataToUpdate) {
return {type: actionType, data: dataToUpdate}
}
And our method would be like:
saveSelection(dataToUpdate) {
dispatch(saveSelectionAction(dataToUpdate))
}
And saveSelectionAction becomes the action creator part. Now comes the dispatch part. So, lets wrap this method with another method which provides dispatch and expose saveSelection
anotherMethodWhichPassesDispatch(dispatch) {
// unleash the power of closure
return {
saveSelection: saveSelection(dataToUpdate) {
dispatch(saveSelectionAction(dataToUpdate))
}
}
}
or more correctly
// Method to map dispatching action to components props
anotherMethodWhichPassesDispatch(dispatch) {
// return list of your created action you want as props
return {
// Name it whatever you want and that will be a void method passing data
saveSelection: (dataToUpdate) => {
// pass the action creator's result which is defined above
// Or, exported from a separate file
dispatch(saveSelectionAction(dataToUpdate))
}
}
}
Now pass this method as argument to connect HOC and that will map saveSelection to a props and provide dispatch to your method.
connect(mapStateToProps, anotherMethodWhichPassesDispatch)(<container>) or we can rename it as mapDispatchToProps.
Now go back to your class's saveSelection method and do like:
saveSelection = (selectedColor) => {
this.props.saveSelection(selectedColor);
}
That's it.

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'})
}
}
}

Resources