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

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.

Related

Is there a setState in Redux RTK?

I am new to Redux RTK and I am kinda confused. Is there a setState using method like in useState of React?
For example I have this code where I fetch some data from an API, and after I create some extraReducers.
But I am wondering, I have a handleChange of a dropdown, but so far I have not figured out how to setState onChange:
export const getPlayers = createAsyncThunk<IPlayerProps[]>('players/getPlayers', async (_, _thunkApi) => {
try {
const response = await axios.get(
'https://6360055fca0fe3c21aaacc04.mockapi.io/player'
);
return response.data;
} catch (error) {
return _thunkApi.rejectWithValue(error);
}
Here is the Slicer:
export const playerSlice = createSlice({
name: 'players',
initialState,
reducers: {
setPlayers: (state, action: PayloadAction<IPlayerProps[]>) => {
state.players = action.payload;
}
},
extraReducers: (builder) => {
builder.addCase(getPlayers.pending, (state) => {
state.loading = true;
});
builder.addCase(getPlayers.fulfilled, (state, action) => {
state.players = action.payload;
state.loading = false;
});
builder.addCase(getPlayers.rejected, (state, action) => {
state.loading = false;
state.errors = action.payload;
})
}
});
export default playerSlice.reducer;
export const { setPlayers } = playerSlice.actions;
And here is where I do a simple setState with target's value:
const handleChange = (e: SelectChangeEvent<string>) => {
setState(e.target.value);
};
Any help would be appreciated! :)
If I understood your question properly, useState is used to save or update data locally in the same file and the data saved won't be accessible globally in the project.
but using redux, All the project will have access to a shared data storage.
Therefore, Redux work in a different way than useState hook of react.
Thus, to update, change, remove or add values in redux, we use dispatch to call the action and force the redux state update.
for example:
let's say you have an action called getPlayers in redux, so the this action when you call it, it should return data based on a specific type and safe it in the redux reducer state.
So, to call that action you should import useDispatch and use it which will fire the redux action, and you should import useSelector which get the data from redux (in other words, whenever you fire an action in redux and it updates redux state, the useSelector will automatically change the data in the file you are using, so the behavior will be the same as setState):
import { useDispatch } from 'react-redux';
//to get the data from redux
import { useSelector } from 'react-redux';
...
//to fire actions in redux which will force update the redux state (selectors)
const dispatch =useDispatch();
useEffect(()=>{
//this will call the action
dispatch(getPlayers());
},[]);
// when you dispatch(getPlayers()) and the new data is fetched, the players in redux will automatically update.
const players= useSelector((state) => state.players);
...
To conclude, if you want to update a variable on click, you should create an action in redux that will update a variable in the state of redux, which can be called using dispatch and update the variable as a useState behavior.
I know this may sound complicated but once you get the architecture, redux can be very helpful in many scenarios, good luck! and let me know if you need anything or if you have any question.
Here is a quick article that should help you understand redux good architecture (Best Redux architecture explained in 5 minutes

React-Redux: how to set the state?

I am trying to understand someone else their code but have difficulty understand the interaction between Redux and React.
On a React page, I invoke a Redux action called getSubscriptionPlan. Inside that Redux action, I see it is able to load the correct data (point 1 below). This uses a reducer, in which I can again confirm the correct data is there (point 2 below).
Then the logic returns to the React page (point 3 below). I now would expect to be able to find somewhere in the Redux store the previously mentioned data. However, I can't find that data listed anywhere... not in this.state (where I would expect it), nor in this.props. Did the reducer perhaps not update the store state...?
What am I doing wrong and how can I get the data to point 3 below?
React page:
import { connect } from "react-redux";
import { getSubscriptionPlan } from "../../../appRedux/actions/planAction";
async componentDidMount() {
let { planId } = this.state;
await this.props.getSubscriptionPlan(planId);
// 3. I can't find the data anywhere here: not inside this.state and not inside this.props.
this.setState({plan: this.state.plan});
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.payment.paymentData !== this.props.payment.paymentData) {
this.setState({
checkout: this.props.payment.paymentData,
plan: this.props.payment.paymentData.plan,
});
}
}
const mapStateToProps = (state) => {
return {
plan: state.plan,
};
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators(
{ getSubscriptionPlan }, dispatch
);
};
export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(Checkout)
);
Redux action:
export const getSubscriptionPlan = (id) => {
let token = getAuthToken();
return (dispatch) => {
axios
.get(`${url}/getSubscriptionPlan/${id}`, {
headers: { Authorization: `${token}` },
})
.then((res) => {
if (res.status === 200) {
// 1. From console.log(res.data) I know res.data correctly now contains the data
return dispatch({
type: GET_PLAN_SUCCESS,
payload: res.data,
});
})
};
};
Reducer:
export default function planReducer(state = initial_state, action) {
switch (action.type) {
case GET_PLAN_SUCCESS:
// 2. I know action.payload, at this point contains the correct data.
return { ...state, plan: action.payload };
default:
return state;
}
}
You are getting tripped up on how Redux works.
Redux does not use react component state. It manages state separately, and passes that state to components as props. When you call getSubscriptionPlan, you asynchronously dispatch an event to Redux, which handles the event and updates store state in the reducer. This state is the passed to the connected components mapStateToProps function, mapped to props, and then passed as props to your component. Passing new props triggers a componentDidUpdate and a rerender of the component.
A few key things here.
Redux does not interact with component state unless you explicitly set state with props passed from Redux.
Redux is asynchronous. That means that when you make a change to state via dispatch, the change is not immediately available in the component, but only available when new props are passed. It's event driven, not data binding. As a result, in your code you woun't see the plan prop in componentDidMount because at the time componentDidMount the call to getSubscriptionPlan hasn't happened.
You should see the prop populated in this.props in componentDidUpdate and in render before the didUpdate.
When working with react, it's best to think of components as basically functions of props with some extra lifecycle methods attached.

How to get the value as props in a different component

In my react application, I have three parallel components. In my first component, I am doing an API call and based on the response I am routing the flow to Validated or Non-Validated Component.
Once the user is routed to validated component, there is a button on click of which user should be redirected to another component which should display the data in API response (first component) as key value pair. I am using Redux for state management.
The issue I am facing is the data is dispatched as an empty object from the store. I am not sure where I am going wrong but when I am debugging the app, I see the the action is not getting dispatched to the store and it's always returning me the initial state.
action.js-
export const setPoiData = (poiData) => dispatch => {
console.log('inside actions');
dispatch({
type: SET_POI_DATA,
payload: poiData
})
}
Reducer.js-
const initialState = {
poiData: {},
}
const reducerFunc = (state = initialState, action) => {
switch (action.type) {
case SET_POI_DATA:
console.log('inside poi reducers');
return {...state,poiData: action.payload}
default: return {...state}
}
}
Component 1-
//API call
Detail Component-
To get the data from store I am doing something like below-
componentDidMount() {
console.log(this.props.poiData)
}
function mapStateToProps(state) {
return {
poiData: state.poiData,
}
}
const mapDispatchToProps = dispatch => ({
setPoiData(data) {
dispatch(setPoiData(data));
}
})
I am not sure where I am going wrong. Can someone suggest me how to proceed ahead on this?
inside componentDidMount() you must call action like this this.props.setPoiData(<your data here>);

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

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

right way to POST data to a server and handle response with redux

I'm very new to react and redux.
Now I want to rewrite my post request with a redux process.
my current request looks like this:
_handleSubmit(event) {
axios
.post('/createUrl', {
url: this.state.url
})
.then((response) => {
this.setState({
shortenInfos: response.data
})
})
.catch((error) => {
console.log(error);
});
event.preventDefault()
}
now I created a store:
export default function url(state = 0, action) {
switch (action.type) {
case 'CREATE_URL':
// maybe axios request?!
return `${action.url}/test`
case 'CREATED_URL':
return `${action.url}/created`
default:
return state
}
}
so where I must use my store.dispatch()? Should I make my _handleSubmit something like this?
_handleSubmit(event) {
axios
.post('/createUrl', {
url: this.state.url
})
.then((response) => {
store.dispatch({
type: 'CREATED_URL',
url: response.data
})
})
.catch((error) => {
console.log(error);
});
event.preventDefault()
}
I think this is wrong? And where I must use mapStateToProps method? Or should I do the axios-request in my CREATE_URL in my reducer?
Introduction
Using React with Redux gives you high freedom on how you can do things. The downside of this is that it can be hard to find out how things should be done properly, mainly because there is no standard or comprehensive guide to the use of the many dependency you need for a properly implemented project. This answer will guide you through the basics with links to references that will help you to find out wheres next and how to deeper your knowledge.
Reducer
Reducers should be pure, meaning that they have no side effects (like making axios requests) and they should always return a new object/array/value instead of changing the previous state. It is also a good practice to use action types as constants. You can place action types wherever you want, but for simplicity I will put them into the reducer's file, but there are better ways to organize them like using ducks.
export const CREATED_URL = 'CREATE_URL';
export default const url = (state = '', action) => {
switch (action.type) {
case CREATED_URL:
return action.url;
default:
return state;
}
};
Asynchronous actions
Everything that causes a side effect should be an action, so XHR should happen there. Because XHR should be asynchronous it is recommended to use a middleware: redux-thunk and redux-saga are two popular solutions. I will go with thunk so install it first.
First (because const has temporal dead zone) you need an action that will "load" the result of the XHR to the store:
import { CREATED_URL } from './reducer';
const createdUrl = url => ({
type: CREATED_URL,
url, // ES6 trailing comma for prettier git diffs
});
Then you can create the action that will fire the XHR, wait for the response then load it to the store using the action created previously. We need to return a function that will receive dispatch as the parameter. This technique is used in functional programming and is called currying.
export const createUrl = url => dispatch => { // with only 1 parameter the parentheses can be omited
axios
.post('/createUrl', { url }) // ES6 Shorthand property name in { url }
.then(response => {
dispatch(createdUrl({
url: response.data,
})
})
.catch(error => {
// #TODO dispatch an action that will show a message
// notifying the user that the request failed
console.log(error);
});
}
Usage in the React component.
Preparation
For ease of use, you need to connect your React component with Redux. react-redux comes to the rescue. Read the API documentation and add the <Provider> component to the root of your React component tree.
Now, in the top of your React component's file, import all the necessary stuff:
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { createUrl } from './reducer';
mapStateToProps and mapDispatchToProps
Then create the two helper functions for connect:
const mapStateToProps = store => ({ url: store.url })
const mapDispatchToProps = dispatch => bindActionCreators({ createUrl }, dispatch)
With the help of mapStateToProps you can subscribe to store updates and inject the important parts of the Redux store to your components props. mapStateToProps should return an object that will be merged to the component's props. Usually we just do something like store => store.url but because our example is so simple that the reducer returns a plain string instead of something more complex in an object, we need to wrap that string into an object over here.
mapDispatchToProps with the help of bindActionCreators will inject the passed actions to the component's props so we can call and pass them down to subcomponents with ease: this.props.createUrl().
The component itself
Now we can create the component itself. I will use an ES6 class to show an example with componentDidMount, but if you don't need that and you have a stateless component, you can go with a function too.
class Example extends React.Component {
componentDidMount() {
// If you need to load data from a remote endpoint place the action call here, like so:
// this.props.createUrl('your-url');
}
render() {
return (
<div>
<div>URL injected from the store, automatically refreshed on change: {this.props.url}</div>
<div onClick={event => {this.props.createUrl('your-url');}}>Click me to fetch URL</div>
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Example)

Resources