How to save redux state per user transaction - reactjs

I have a redux react web app booking court app and I want to manage/store redux state based on the user unique id that is coming from the server when the user initials the booking. Currently my initial state looks like this:
const initialState = {
payload: [],
loading: false,
}
I have a reducer like
const courtReducer = (state =initialState, action: any) => {
switch(action.type) {
case MAKE_BOOKING:
return {
...state,
loading,
payload: action.data
};
other action...
}
}
I am not sure if I am making sense.

If you are trying to figure out how to store state, maybe the "Sending Data with Thunks" section of the Redux documentation will help. It's part of a tutorial which should leave you with a pretty good idea of how to integrate async API calls with reducers.

Related

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.

React Redux - How to do a proper loading screen using React and Redux on url change

I am working on a web app that uses React + Redux. However, I am struggling with the loading screens. Basically, my initial state for the redux store is this.
const initialState = {
project: {},
projects: [],
customers: [],
vendors: [],
customer: {},
vendor: {},
loading: false
};
An example of one of my reducers is this
const fetchSalesProject = (state, action) => {
return updateObject(state, {
project: action.payload,
loading: false
});
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.FETCH_SALES_PROJECT: return fetchSalesProject(state, action);
default:
return state;
where updateObject is this
export const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
}
}
here are my actions (axiosInstance is just a custom addon to axios to include the necessary headers for authentication, but works exactly the same like a normal axios api call)
export const fetchSalesProject = (id) => (dispatch) => {
axiosInstance
.get(`/sales-project/detail/${id}/`)
.then((res) => {
dispatch({
type: FETCH_SALES_PROJECT,
payload: res.data,
});
})
.catch((err) => dispatch(returnErrors(err.response.data, err.response.status)));
};
export const showLoader = (area) => (dispatch) => {
dispatch({
type: SHOW_LOADER,
area: area ? area : true
});
};
where the param area is just in the case where loading is meant to be rendered only on a specific area on the page (not relevant for this question hence when showLoader is called, no params would be passed in and hence the area would be set to true for this question)
this is my component
componentDidMount() {
this.props.showLoader()
this.props.fetchSalesProject(this.props.match.params.id)
};
this is my mapStateToProps
const mapStateToProps = state => ({
project: state.api.project,
loading: state.api.loading
})
this is my conditional rendering
render() {
return (
!this.props.loading ?
{... whatever content within my page }
: { ...loading screen }
Basically, the issue I am facing is that, when i first load this component, there would be an error, as my this.props.project would be an empty {} (due to initialState). The reason why is that before the showLoader action is dispatched fully by componentDidMount, the component renders, and hence since this.props.loading is still set to the initialState of false, the conditional rendering passes through and the this.props.project would be rendered (however since it is empty, the various keys that I try to access within the rendering would be undefined hence throwing an error).
I tried various methods to try to overcome this, but none seem like an optimum solution. For example, I have tried to set the initialState of loading to an arbitary number, like eg. 0, and only render the page when this.props.loading === false, which is only after the data is fetched. However, the issue arises when I go onto a new page, like for example, another component called CustomerDetail. When that component is mounted, this.props.loading would be false and not 0, as it was previously manipulated by the previous component that I was on (SalesProjectDetail). Hence, that same error would come up as the conditional rendering passes through before any data was actually fetched.
Another method I tried was to use component states to handle the conditional rendering. For example, I only set a component state of done : true only after all the dispatches are completed. However, I felt like it was not good practice to mix component states and a state management system like Redux, and hence was hoping to see if there are any solutions to my problem that uses Redux only.
I am looking for a solution to my problem, which is to only render the contents only after the data has been fetched, and before it is fetched, a loading screen should be rendered. All help is appreciated, and I am new to React and especially Redux, so do guide me on the correct path if I am misguided on how this problem should be approached. Thanks all in advance!
there can be more ways to do it. But to go ahead with your scheme of local state...
I think there is no problem if you use local state to keep track of load complete or underway.
You can have a local state variable as you already said.
Before dispatch set that variable to loading=true then use async/await to fetch data and afterwards set loading back to false.
I think it will work for you.

Is there a way to persist a logged-in user in React/Redux without using redux-persist or similar middleware?

I am setting my Redux state to be the user object of the user that is logged in (coming from my backend database).
This is what that reducer looks like:
const initialState = {
user: {},
}
export default function(state = initialState, action) {
switch(action.type) {
case LOGIN_USER:
return {
...state,
user: action.payload
}
...
With the payload from LOGIN_USER being the user object of the user who logged in.
Now using this state tree, every time I refresh or hit a new route the state is wiped and I am left with an empty object for the 'user' state. I know that there is middleware like redux-persist that aims to solve this issue but I can recall seeing/hearing of projects that do not use this middleware to persist their logged in user state.
Is there any way that I can persist my logged in user throughout refresh/route changes using my current build? Or would I need to somehow change my build to get it to work?
Thanks a lot!
The simplest and most trivial approach is:
store.subscribe(() => {
const {user} = store.getState();
localStorage.set("user", JSON.stringify(user));
});

can i chain redux promsie action in react component?

I have a redux action which get data from server my action is like this
export const getFakeData = () => (dispatch) => {
return dispatch({
type: 'GET_FAKE_DATA',
payload: {
promise: axios.get('/test'),
}
});
};
my reducer is like this
const reducer = (INITIAL_STATE, {
[GET_FAKE_DATA]: {
PENDING: () => ({
isLoading: true,
}),
FULFILLED: (state, action) => {
const { data } = action.payload.data;
return {
...state,
data,
error: false,
isLoading: false,
};
},
REJECTED: () => ({
isLoading: false,
error: true
}),
});
I want to show success alert after my action sent, is below code breaks the principle of redux about one way flow?
this.props.getFakeData().then(() => {
this.setState({
showAlert: true
});
});
According to your use-case, it's perfectly fine to keep showAlert flag in the component's local state, instead of the Redux store.
Here's what Redux official documentation stands for:
Using local component state is fine. As a developer, it is your job to
determine what kinds of state make up your application, and where each
piece of state should live. Find a balance that works for you, and go
with it.
Some common rules of thumb for determining what kind of data should be
put into Redux:
Do other parts of the application care about this data?
Do you need to be able to create further derived data based on this original data?
Is the same data being used to drive multiple components?
Is there value to you in being able to restore this state to a given point in time (ie, time travel debugging)?
Do you want to cache the data (ie, use what's in state if it's already there instead of re-requesting it)?

connect react-redux store to express + mysql backend

I am getting used to redux with react and i have worked through a few tutorials now. I'm left confused on how I should get data from my express api (where i get data from db) to the redux store.
Say i have a file in my express backend users.js where I connect to my mysql db and return all users in my database. So in my reducers folder i have an index.js file looking something like this
/reducers/index.js
// would i do a fetch(/users) here to populate users array with data from db?
const initialState = {
users: []
};
const rootReducer = (state = initialState, action) => {
switch(action.type) {
case FETCH_USERS:
return { ...state, users: [...state.users, action.payload] };
}
}
or would the proper way be to have this return the empty array and in my component on componentDidMount(fetch(/users)...) and set state.users to my results from that fetch?
I haven't found anything solid on best practices for react/redux with express api so any answers aswell as links to clear up my confusion would be much appreciated
EDIT** If you're reading this and have similar confusion on making api calls with react/redux I have found that the best practice is to have fetch calls in action creators.
Simply import and call your action creator in your component and pass it the params you would like. For this example I put a function in my actions file called fetchUsers() where I use axios to fetch data from server.
You should fetch users in your componentDidMount() and then in the return of promise, you can dispatch the users to your store.
componentDidMount(){
fetch(url).then((response)=>{
if(response.data){
this.props.dispatch({
type:"SET_USERS",
payload: response.data
})
}
})
And you should connect() your component to store to get the dispatch() in your props.

Resources