React-native + Redux(Combine Reducers): Create state structure - reactjs

I am developing a react-native app and new to redux. I've 3 screens and almost all state of all screens dependent on each other.
e.g First screen scans the product, second screen takes customer details and third screen displays the summary from of products and customer.
I am hitting the wall since 2 days to create the state structure and I end up with combineReducers. But in combine reducers, each reducer maintains its own slice of state and doesn't let me access or update the state of another reducer.
So,
Q1. Should I continue with combieReducers or should maintain single reducer only?
Q2. If combineReducers is OK, how should access or update the same state along all reducers?

for u r first question yes u have to combine because combineReducers will give all reducers data in single object
example:
const rootReducer = combineReducers({
posts: PostsReducer,
form: formReducer
});
inside component
function mapStateToProps(state) {
return { posts: state.posts };
}
export default connect(mapStateToProps, { createPost })(PostsIndex);
in the above code through Redux connect you can access state which is produced by combine reducera like this.props.posts inside your component
To update the you need to triger an action which go through all reducers and based on type you can update the state
example:
export function createPost(values, callback) {
const request = axios
.post(`${ROOT_URL}/posts${API_KEY}`, values)
.then(() => callback());
return {
type: CREATE_POST,
payload: request
};
}

using middleware you can achieve the funtionality
export default ({ dispatch, getState }) => next => action => {
next(action);
const newAction = { ...action, payload: "",state:getState () };
dispatch(newAction);
};

Related

How to save / get a value with the redux store?

Hello Stackoverflow Community,
I was hoping you could help me with the following logic. I would like to return from a custom-built hook the last item the user selected in a onClick function.
const useActiveWeb3React = (): Web3ReactContextInterface<Web3Provider> => {
const { chainId, account, ...web3React } = useWeb3React()
const { solanaAccount, solanaChainId } = useSolanaWeb3React()
const activeChain = "if the user's last selected chain is Solana" ? solanaChainId : chainId
const activeAccount = activeChain === ChainId.SOLANA ? solanaAccount : account
return { chainId: activeChain, account: activeAccount, ...web3React }
}
OnClick handler that would send a network change request to either MetaMask or Phantom Wallet when user selects one of the chains.
const handleSelection = (network: ChainId) => {
onDismiss()
onNetworkSelect(network)
}
What I would like to accomplish is that if the user selected in the app ChainId.SOLANA I would like to update activeChain variable in the useActiveWeb3React hook so that the whole app also knows user now wants to be on Solana. And then if the user switches back to Ethereum I want to update the activeChain to reflect the users last selection. Also I would like to stay at that chain if user refreshes the app.
I have access to redux store in the app.
How would you do it?
Thanks for suggestions!
You need three different things.
Save a value inside the state
Get a value inside the state
Persit the state (so when user refresh the page you still have this information)
How to save a value in the redux store ?
To avoid boilerplate redux team recommend to use redux-toolkit as the standard approach.
You can create a part of the state of your app with createSlice
This will help you to create actions creator and reducers
userWallet.slice.ts
import { createSlice } from '#reduxjs/toolkit'
import type { PayloadAction } from '#reduxjs/toolkit'
interface UserWalletState {
chainId: ChainId | null
}
const initialState = { chainId: null }
const userWalletSlice = createSlice({
name: 'userWallet',
initialState,
reducers: {
setChainId(state, action: PayloadAction<ChainId>) {
state.chainId = action.payload
},
},
})
export const { setChainId } = userWalletSlice.actions
export default userWalletSlice.reducer
We create a slice named: userWalletSlice and we export the generated reducer, don't forget to add it as your reducers now:
rootReducer.ts
import userWalletReducer from './userWallet.slice.ts'
export const rootReducer = combineReducers({ userWallet: userWalletReducer })
When create this reducer inside userWallet.slice.ts:
setChainId(state, action: PayloadAction<ChainId>) {
state.chainId = action.payload
},
Every time we dispatch the action setChainId. This will take the payload and update state.chainId.
When we create setChainId reducer this will automatically create an action creator that you can export.
export const { setChainId } = userWalletSlice.actions
You'll be able to dispatch it like this:
dispatch(setChainId(chainId))
Then inside your callback you can just do:
const handleSelection = (network: ChainId) => {
onDismiss();
dispatch(setChainId(network));
}
Now that you can put this chainId into your state, you need to retrieve automatically when it change.
How to get a value from redux store ?
To get values inside the store we use selectors.
You can use createSelector to create a memoized selector.
userWallet.selector.ts
export const userWalletSelector = (state: AppState) => state.userWallet;
export const chainIdSelector = createSelector(userWalletSelector, userWallet => userWallet.chainId)
So now inside your custom hook you can get this value with the following line.
const activeChain = useSelector(chainIdSelector);
How to persist redux state to retrieve it when I refresh a page ?
There are multiple way to persist state of you app. For your kind of information like chainId you can use tool like redux-persist to save part of your state inside the local storage for example

how action contacted the reducer to change the state of the object

I am sorry it might be a silly question but i want to know i have redux store first let me explain the data flow there
The data is getting store at a global level state which i have in my store.js which have been declare in my productReducer.js There i define a switch statement and there i alter the state of the product
productReducer.js
my code here
import {
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_FAIL,
CLEAR_ERROR
} from '../constants/productConst'
export const productReducer = (state ={product:[]},action)=>{
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return{
laoding: true,
product:[],
}
case PRODUCT_LIST_SUCCESS:
return{
laoding: false,
product:action.payload.products,
productsCount:action.payload.productCount,
}
case PRODUCT_LIST_FAIL:
return{
laoding: false,
error:action.payload,
}
case CLEAR_ERROR:
return{
...state,
error:null
}
default:
return {
...state,
}
}
}
i have action productAction.js
import axios from 'axios'
import {
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_FAIL,
CLEAR_ERROR
} from '../constants/productConst'
export const getProduct = () => async (dispatch) =>{
console.log("Been executed at the action")
try {
dispatch({type:PRODUCT_LIST_REQUEST})
const {data} = await axios.get("api/v1/products")
dispatch({
type:PRODUCT_LIST_SUCCESS,
payload:data,
})
} catch (error) {
dispatch({
type:PRODUCT_LIST_FAIL,
payload:error.response.data.message,
})
}
}
export const clearError =() =>async (dispatch)=>{
dispatch({type:CLEAR_ERROR})
}
Let me Sum up my question when i neeed to update the state from the frontend i call the action but there is no way the action and the reducer connected together how the state of the product is altered in my case
To answer your question "how do actions contact the reducer to change the state of the object?":
The overall redux store setup enables this, especially how you registered your productReducer.
Let's go through the typical flow to illustrate how everything is connected:
Somewhere in a React component, a user-interaction (e.g. button click) or something automatic dispatches the async getProduct() action. This is possible because getProduct is either a prop of the component (Redux' connect API) or you're using the useDispatch hook.
The store setup knows that PRODUCT_LIST_SUCCESS is handled by your productReducer. We go through the switch statement and now state.product.product holds an array of products (careful with the naming there btw, plural vs. singular).
Any React component interested in state.product.product is now notified that the state has changed. They're getting notified because either the connect API (mapStateToProps) or useSelector connects the state with the mounted React component. The products can now be (re-)rendered, or clicked on, etc..
Action
An action is a plain JavaScript object that has a type field. You can think of an action as an event that describes something that happened in the application.
Reducer
You can think of a reducer as an event listener which handles events based on the received action (event) type.
By using dispatch() you are dispatching the event and then comes the following in the reducer logic:
Check to see if the reducer cares about this action
If so, make a copy of the state, update the copy with new values, and return it
Otherwise, return the existing state unchanged
If you are interested about more then please have a look into official redux documentation, there is really everything you will need.

How can I integrate React-Redux with Firebase?

I created a react app using create-react-app and configured a redux store with reducers. I also added firebase and my project works fine. The components can trigger an action that fetches a collection from firestore, and it in return, updates the redux store.
What is the best way to integrate firebase and redux store?
The way I am currently doing it, is to have a separate action that triggers the fetch/delete/onSnapshot from firebase, and handing a reference to dispatch so that firebase function can take its time executing the command, then it can call dispatch with an action that updates the store.
But I wanted all of my actions in a single file, for a better (separation of concerns). Therefor, firebase can call dispatch but the action creator lives in my actions.js file. This way, I can later decide to change the action names in a single file, if I decided to do that.
The problem with this approach, is I will require a separate action to trigger the async function with firebase, and another action creator for when the promise is fulfilled.
What is a better approach to what I am doing?
store.js
const rootReducer = combineReducers({
cards: cardsReducer,
});
const store = createStore( rootReducer , {}, applyMiddleware(thunk));
export default store;
myFirebase.js
// I need this to be called from an action in actions.js
// therefor, I am exporting it, and also, I am handing it dispatch
// so it will call store.dispatch once data is ready
export const fetchCardsFromFirebase = async (dispatch) => {
const cardsCollection = collection(db, "cards");
const cardsSnapshot = await getDocs(roomsCollection);
const cards = roomsSnapshot.docs.map(doc => ({ ...doc.data(), id: doc.id }));
// here I can either explicitly dispatch an action
/*
dispatch({
type: CARDS_FETCHED //this constant string will have to be imported
payload: cards
});
*/
// or I can let an action in actions.js do the above:
dispatch(cardsFetched(rooms)); //this function is imported from actions.js
}
actions.js
import { FETCH_CARDS , CARDS_FETCHED } from "./types";
import { fetchCardsFromFirebase } from "../myFirebase";
export const fetchCards = () => async (dispatch) => {
fetchCardsFromFirebase(dispatch); // give firebase access to dispatch
dispatch({
type: FETCH_CARDS,
payload: {message: "fetching cards... please wait"}
});
};
const cardsFetched = (cards) => ({
action: CARDS_FETCHED,
payload: cards
});
Generally, this is a very old style of Redux - modern Redux does not use switch..case reducers or ACTION_TYPES and switching to modern Redux will proably already save you 50% of your code.
That said, the official Redux Toolkit (RTK) also comes with RTK-Query, which is a data caching abstraction that should also work fine with firebase and will generate reducers, actions and even hooks automatically for you. (Hint: with firebase you will need to use queryFn). That would save you a lot more code as well.
I would recommend you to follow the official Redux Tutorial which first shows modern Redux and in later chapters goes into RTK Query.

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.

Redux Thunk getState() is returning all states

Just discovered that I can use the getState() to pass state values to action creators. However I am noticing that getState() is returning all the combined states rather than the one specified in the argument. What am I doing wrong, as I don't suppose this is the correct behavior?
Reducer:
import { combineReducers } from "redux";
import { reducer as reduxForm } from "redux-form";
import authReducer from "./authReducer";
export default combineReducers({
auth: authReducer,
form: reduxForm
});
Action creator snippet:
export const handleProviderToken = token => async (dispatch, getState) => {
let testState = getState("auth");
console.log(testState);
const res = await axios.get(`${API_URL}/api/testEndpoint`);
dispatch({ type: FETCH_USER, payload: res.data });
};
The console.log(testState) is showing me the entire state tree object (auth and form, rather than just auth).
Quoting redux-thunk documentation
The inner function receives the store methods dispatch and getState as
parameters.
and quoting from redux's documentation
getState()  Returns the current state tree of your application. It is
equal to the last value returned by the store's reducer.
So the getState you pass thanks to redux-thunk, is actually redux's getState() function itself and thus it is the default behavior.
To get a specific value from your state tree, you can either use one of the following
const { auth } = getState()
// OR
const testState = getState()
const auth = testState.auth
It is the correct behavior. From docs:
Returns the current state tree of your application. It is equal to the
last value returned by the store's reducer.
It is correct behavior. You will need to pick the reducer key from the complete state.
export const handleProviderToken = token => async (dispatch, getState) => {
let testState = getState();
let authReducer = testState.auth;
const res = await axios.get(`${API_URL}/api/testEndpoint`);
dispatch({ type: FETCH_USER, payload: res.data });
};
What works in similar case for me is
const lastFetchedPage =getState().images.lastFetchedPage;
I do not know how it goes with guidelines, however.

Resources