I am new to React and want to understand the difference from classic MVC.
I want to create a simple components that loads some data initially and renders let say a grid.
On some state or prop change it will reload the data and re-render.
What is the best approach in react from below two options?
using the lifecycle events to load the data, update some state and render while in another event will show some loading opacity.
Work with redux and react-redux? but in all example I cant see API calls.
Is this the role of a middleware (Thunk?)?
Will appropriate an explanation.
Both the approaches are correct. It depends on your use case. If you can avoid using redux in your app, use the lifecycle methods to make API calls (also called subscriptions in react documentation). If you think your app has many components and different components needs to share a state, then use redux.
You should also look at React hooks https://reactjs.org/docs/hooks-reference.html
You can use Effect Hook https://reactjs.org/docs/hooks-effect.html to make API calls and update your component's state.
Update:
Both Thunk and Sage are used to manage side effects in your application (making API calls from here). I've used saga, I don't know much about thunk.
How you would use redux-saga:
Say if you want to get some data for display on a button click, this is how it works:
On button click you dispatch an action, say GET_DATA
Your redux reducer will change some state on this particular action, say isLoading=true
You can use isLoading in your component to show a spinner/overlay
At the same time saga will listen to GET_DATA action and make the API call
If success, from Saga you'll dispatch an action, say GET_DATA_SUCCESS with the data from API call
Reducer will change isLoading=false and set the data in state, say apiData = { ... }
If failure, from Saga you'll dispatch an action, say GET_DATA_FAILED with the error code/message
Reducer will change isLoading=false and set the error in state, say apiError = { ... }
You can now use isLoading=false in you component to remove spinner and display the data (apiData) or error (apiError) in you component.
You can go through this tutorial to learn more https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html
Related
I have a react project configured with redux for state management.
For api calls I use a file called axiosInstance. In this file I need to have access to redux store (and I have access importing store and using getState - store.getState()). Now the problem is that I want also to update the redux state from this file (axiosInstance).
How I can update redux store from this file (which is not a react component) in a efficient method?
Okay, I assume that you are using Axios for the network calls.
tl;dr
Use store.dispatch(action) and action to make changes to store state
Yes, the store can be accessed with store.getState(), you can also change state with store.dispatch(action). as per the docs
Dispatches an action. This is the only way to trigger a state change.
The store's reducing function will be called with the current getState() result and the given action synchronously. Its return value will be considered the next state. It will be returned from getState() from now on, and the change listeners will immediately be notified.
This happens because of the functional paradigm that redux follows (google for more).
Now, the action has to be defined with an actionCreator, or simply you could call .dispatch() with an inline object with one string property as {type: 'name-of-action'}. This prompts the store to change the state.
You may require redux-thunk for async actions.
Conclusion:
Let's save some trouble here as you may have many calls to the store, you can create a context that handles the AxiosInstance (as there is already a different file for that).
Make a context in the AxiosInstance file and then you can start dispatching actions as per the network responses. this will save you a lot of trouble as the state is updated by redux without actually looking at it much.
Let's say I have this code below:
switch (action.type) {
case "GET_LIST":
// update state
break;
case "GET_LIST_SUCCESS":
// update state
break;
case "GET_LIST_ERROR":
// update state
break;
default:
return state;
}
Before this, I've already set-up some action creators (with a thunk middleware for async requests). So the flow would be either GET_LIST > GET_LIST_SUCCESS OR GET_LIST > GET_LIST_ERROR.
Now in my React component, I want to do something depending on which action type was dispatched in the end (either success or error).
Is it a good practice to use the action type themselves or should I just use the store state for my condition? In this example, my store would have an error property, which I can use to detect whether there's an error or not.
So it's either (using action type):
if (actiontype === "GET_LIST_SUCCESS") {
// do something
}
OR (using the store state)
if (this.props.list.length > 0) {
// do something
}
Which is a better practice and why?
Just my opinion, but one advantage of using Redux over storing data in component state is that you separate the concern of how to handle data updating from view layer (React) to Redux; React component should only know if the data has been changed, not how it is changed. This de-couples the data layer and view layer in frontend, and helpful when you need to execute a chain of actions to update state (e.g. fetching from API).
If you want to expose current Redux action type to React component, you have to log the action somewhere in your app, since I don't think Redux exposes current action to view layer. In addition, this also reveal too much info of how Redux handles data flow.
Common practice is saving fetching state in boolean, and error log, e.g. isFetching and fetchListError, update those values along the fetching process, and pass those value to view layer for display.
From my understanding, we have react app with components and redux app with store. We use
another library react-redux to bind react app and redux app. React-redux helps to use redux store state in react component and helps to dispatch actions to redux store from react component.
For handling asynchronous operation libraries like Redux-thunk is used and depending on the result of the asynchronous operation, these libraries dispatch action.
Answer to your question: React is not aware of which action was dispatched in Redux store as it is handled by thunk middleware. But using React-redux, React can know what is the current Redux store state after the action was dispatched by thunk middleware. So I think you shoud go with the second option. First option might be possible but it will need alot of hacking than simply using the first one.
I've been learning React for the past few weeks and I'm struggling with a way to fetch a collection of data from server and presenting it using Redux.
What I have:
I have a <MoviesList /> component which renders a list of <Movie /> components inside it.
What i need:
I need to fetch these movies from a server and store it in my state.
What I'm struggling with:
If I create a container component, I'll use mapStateToProps to pass the movies from the state down to my <MoviesList /> component, but first I need to fetch it from the server, so I thought that I should do it in one of the container component's lifecycle hook, but then I'll have a movies prop (which should be validated with PropTypes) both in my <MoviesListContainer /> and <MoviesList />, which will force me to write a big prop validation in both components, which doesn't look right at all for me.
This wouldn't be a problem if I were to simply spread the props in the container using a function component, but since I need to fetch the movies in the lifecycle hook, it needs to be a class and I need to specify the props in both files.
So what would be the best way to achieve this?
Should I fetch these movies in a different place? Should I take a different approach for fetching it and avoiding these duplications? Or maybe I really should do the validation in both files?
Thanks everyone!
What I have done in the past is to have a function property which is called in the componentDidMount lifecycle event:
componentDidMount() {
this.props.onMount();
}
That property is mapped in the container using mapDispatchToProps to a thunk or some other async action (see the Async Actions documentation).
For a more sophisticated alternative see Redux First Router which allows to dispatch async actions from the router.
#JCE is right, you need to look into Async Actions. If you want to fetch the data and store it in Redux before passing it to your component, you must use a side-effect library (redux-thunks, redux-sagas, etc). The reason is that Redux actions are synchronous by nature and fetching an API is an asynchronous operation. The side-effects libraries will help you achieve that.
I am working on my first React Redux project.
The project depends on a server to get its data and therefore there is also a server API that needs to be called in order to get those data.
My question is about initialising state.
There are 2 ways I can think of initialising state:
1.) First pass an empty object {} as the initialised state of the Redux store then inside componentDidMount that is where I call the API to access the data in the server and then update the state of the store/app then.
2.) In the reducer of the Redux app call all relevant server method (e.g. getCategories(), getPosts(), etc.) then compose a object with all of the data e.g. {categories: categories, posts: posts} then pass this object to the reducer as the initial state.
Which way is the recommended way for Redux when dealing with data stored in the server?
If there is a 3rd or 4th way that is the Redux way or the recommended way then please share your knowledge. Thank you.
The way I work with data coming from the back end, and as far as I know is also the suggested way, is this:
In the componentDidMount method call a thunk action creator. you can read more about them in this link.
Inside that thunk action creator you first dispatch a REQUEST action, then perform a fetch to access the back end and when the response arrives you handle it on either the success or error handlers. Depending on the handler executed you dispatch either a SUCCESS or an ERROR action (and you attach to it all the relevant information that is coming from the back end so the reducer can access it).
In your reducer you write code to handle all the above actions (REQUEST, SUCCESS and ERROR). Each handler will transition your state, for instance the REQUEST can set an isFetching flag to true that will let you show a spinner in the UI, and the SUCCESS can set that flag to false and populate the state with the data coming from the back end and passed to the reducer using the action dispatched.
Once your reducer is updated, you will access that updated state from the UI, for instance using the connect react-redux function.
Regarding the initial state, it should represent a default state. for instance, it will have the isFetching flag in false and, if you are fetching a list of foods from the back end, then that list could be an empty list in your initial state. This is just a for instance of course. You need to set an initial state that makes sense to your app.
I hope this helps you.
The first way is the better way of initializing state. Your component makes the necessary API calls in componentDidMount and passes the data to Redux as payload of actions which the reducers use to update the state of your application.
The second way is not advisable. According to the Redux docs:
The reducer is a pure function that takes the previous state and an action, and returns the next state.
(previousState, action) => newState
...
It's very important that the reducer stays pure. Things you should never do inside a reducer:
Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().
Edit:
You can also use thunk middleware and async actions to do API calls as explained in the Redux docs and #DiegoTArg's answer.
I came across interesting article (Link at the end of post). Author of the article states, that they treat redux store as client-side database and UI logic does not fit there (If it's not needed for unrelated components), even for data fetching purposes. For example we want to show some loading spinner when fetching some data:
async componentDidMount() {
this.setState({isLoading: true});
await this.props.fetchSomeData();
this.setState({isLoading: false});
}
We fire async thunk action that fetches some data that is needed for more than one component or we want to cache that data, even when that component is unmounted.
The only component that is concerned about loading state is the one in which we fire thunk action, other components are not concerned about state of loading. But I always see redux examples with async actions creators which fire REQUEST/SUCCESS/FAILURE actions types and reducers bloated with loading states even if they're are used in one component.
I can see some cons of this code, that some state lives in component and some in redux, but pros are that redux store is not bloated with state that is not needed for other components, also we can avoid the verbosity of redux.
So my question would be, what are cons of this state separation regarding this particular example ?
Article: https://dev.bleacherreport.com/3-things-i-learned-about-working-with-data-in-redux-5fa0d5f89c8b
(Also interesting discussion in article comments)
IMO, some of the cons of your state separation approach is that:
If there is complex API fetching behind the scene ( fetch A then fetch B then fetch C) redux-thunk would be inadequate and you will have to use redux-saga or redux-observable and together with them, the REQUEST/SUCCESS/FAILURE styles.
If, in the future, there are other components need to listen to the 'loading' state or worse need the same data but there is no guarantee that the order of display them will be the same ( A mount and fetch data and then B mount before the data is fetched, and B fetch again ), you will have to refactor all the structures. And that doesn't count the case when you need to CANCEL a fetch.
If your component unmount before the data is fetched, the "this.setState" call will result in a null reference error.
In general, I agree that UI state should be put in the component, but global state must always be stored in the Redux store and your isLoading is not totally a UI state but a state of the request that your component are listening to.