Correct way to set state after redux action - reactjs

What's the correct way to set the state after a redux action is called? Before I used to do it using the componentWillReceiveProps but now as I read, it's considered legacy and should be avoided. The question is, how should I set the state without this method?
I'll give you an example:
componentDidMount() {
this.props.getProfile();
}
Let's say I call this getProfile action in componentDidMount() and as a result I get a profile object which among other things, contains a simple string field, let's say color: "red".
Based on this color field that I got from calling getProfile(), I want to setState and call another redux action without performing any click action or anything. I want to call this.props.getFavoriteColor(this.state.color)
What's the best practice?

static getDerivedStateFromProps(nextProps, prevState)
The new function which main responsibility is ensuring that the state and props are in sync for when it is required. It’s main job is replacing componentWillReceiveProps
checkout the docus: https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops

I would do something like below:
async componentDidMount() {
const {color} = await this.props.getProfile();
this.props.getFavoriteColor(color)
}

Related

Reactjs, I need some understanding of this.setState and prevState

componentWillReceiveProps(nextProps) {
// Check to see if the requestRefresh prop has changed
if (nextProps.requestRefresh !== this.props.requestRefresh) {
this.setState({loading: true}, this.updateData);
}
}
As far as my understanding goes, this lifecycle hook is called when a Component's properties are updated by the parent.
This piece of code checks if the requestRefresh property is different (one to update and one that it currently has)
Now what i dont understand is this , this.updateData);
why is this with the setState method. Please help me understand
Secondly where does prevState come from and which lifecycle hook can update it
the setState method can be called with a callback as its second parameter, mainly to handle operations which need to be done when the state is fully updated.
In your example this.updateData is the callback.
setState's first argument is also a function, with the signature (prevState, props) => stateChange, this is allowing you to get access to the previous state when performing updates.
You may want to check the official doc for further details :
https://reactjs.org/docs/react-component.html#setstate
The React maintainers discourage the use of componenetWillReceiveProps.
From the React docs:
It is recommended that you use the static getDerivedStateFromProps lifecycle instead of UNSAFE_componentWillReceiveProps. Learn more about this recommendation here.
To answer your question: prevstate is the previous state, so it is the state of your component before new props are received which in turn might chnage the new state. You can also hanlde prevstate in the static getDerivedStateFromProps method:
static getDerivedStateFromProps(nextProps, prevState)
getDerivedStateFromProps is invoked after a component is instantiated as well as when it receives new props. It should return an object to update state, or null to indicate that the new props do not require any state updates.

React native redux loading state order

I want the getKey function to wait for the updated state from friendExist.
Currently getKey is set to false as the initial state however friendExist should set the boolean to "true". But it seems that "this.props.getKey(this.props.exist)" dosen't wait "this.props.friendExist(uid)" to update before it runs the code.
componentDidMount(){
const uid = 'MZdKuFpGmGRntb0nyF4PO0f6kco1';
this.props.friendExist(uid);
//friendExist should update this.props.exist to true
this.props.getKey(this.props.exist)
}
Both friendExist and getKey are actions that dispatch the new state.
Your function componentDidMount is a synchronous function, so it will execute inside code without waiting for any unrelated functions. Unless you change its type to asynchronous, and tell exactly how you want it to wait, this.props.exist will never be true at the time this.props.getKey is executed.
An alternative, simpler solution is taking the update of this.props.exist at componentWillReceiveProps, and execute this.props.getKey in there instead
componentDidMount(){
const uid = 'MZdKuFpGmGRntb0nyF4PO0f6kco1';
this.props.friendExist(uid);
}
componentWillReceiveProps(nextProps, nextState) {
if (nextProps.exist) {
this.props.getKey(nextProps.exist)
}
}
I assume you are using react-redux mapStateToProps() to get the updated state. This behavior is expected as react-redux uses react's setState() internally, which is asynchronous, meaning your state may not map to the props on the same time you dispatch the action.
I suggest creating a function getKeyIfFriendExist() instead which does the dispatching and checking of the latest state inside.

ComponentDidMount and dispatching an action

I do have a React component that loads when user clicks on a ReactRouter Link such as http://mysite/page/:id.
Inside the ComponentDidMount method I'm using the 2 following actions:
postsSetPage(page)
should be a SYNC action that changes the page right away in the store).
I'm passing it the id given from ReactRouter as page.
postsFetch(args):
is an ASYNC action that fetches data from API.
So my code looks like this:
componentDidMount() {
this.props.postsSetPage(this.props.match.params.id);
console.log(this.props.posts.query); // <========== this will not be updated
this.props.postsFetch(this.props.posts.query);
}
this.props.posts.query contains all arguments to apply filter/ordering/pagination on my API. It looks like this:
this.props.posts == {
records: [],
query: {
page: 1,
arg1: "asdasd",
argN: "blabla",
},
}
THE QUESTION:
The code inside componentDidMount doesn't work because after I set the page this.props.posts.query hasn't updated yet (console.log is confirming) and when I call fetch action query doesn't contain the correct page.
So aren't actions such as setPage suppose to be sync?
All help will be greatly appreciated!
setPage as a redux action is synchronous, but the resulting setState which happens due to change in redux store might not be (it's better to assume setState is never synchronous).
And if your setPage action goes through a middleware which does some async stuff (fetching data from api) and then forwarding the action, then the action itself wont' lead to setState call synchronously, because redux store would only change once the api data comes back.
But i guess you are facing the first problem.
One way to take care of your problem can be to do the fetch in either the action (thunked), or do it in a middleware.
As per React, this.props are the immutable object means you can not update it programmatically.
Here you will have to use this.state

Redux - Set state by depending actions

I want to achieve the follow sequence of my data flow:
Get the record-id of react-router
Set this id as state property
If this property is set, all other dependent components, with it's own reducer and actions should get their data depending on the id
componentWillMount() {
this.props.actions.setItemId(this.props.params.id);
if(this.props.id){
this.props.fetchItemInformations(this.props.id);
}
}
I am aware that this flow won't work but I've no idea how to implement it.
Many ways to do this but if your problem is due to async issues then you can use middleware such as Redux Thunk. This will essentially allow you to chain your actions i.e. once the id is set then dispatch another action to do X.
See here - https://github.com/gaearon/redux-thunk
Alternatively, you could do your if statement within the componentWillReceiveProps () lifecycle event and check if it has been set there. If it has then dispatch your next action...
If I understand your question right, you want to fetch the property record-id from react-router (This is a url hash I assume?).
You can directly pass the this prop to all the children (as a prop) and handle the value in the children as you wish. To do this,
Pass the prop as well when rendering { this.props.children }; something like
{ React.cloneElement(this.props.children, { record-id: record-id }) }
This I'd say is the best practice; if you want to pass the record-id to other components (that are not direct children) of your parent component; you need to fire an action and handle the action to update relevant reducer states. Hope that helps.

Correct way to use API with React/Redux

I'm a bit confused about the component lifecycle in React. Consider a component that loads its initial state by dispatching the action, getTodo() and updating the state of the entire app
componentDidMount() {
var _this = this;
axios.get('/api/todo').then(function(response) {
_this.props.actions.getTodo(response.data);
}).catch(function(err) {
console.log(err);
});
}
What can I use in componentWillUnmount() to prevent the memory leak? If I choose to do this, how will it be possible to update the state when I come back to the page from another page with this method?
Also, is it a better to just store the props that I need for the page as the state and updating the state instead? My concern with this approach is that it just doesn't update the state of the entire app and other components that might need the same props will have to go through the same unnecessary process, which could be avoided by using the first approach.
You should avoid doing api call in a component. React offers an interface "nothing" more.
You should dispatch an action. In fact an action creator can be used (check redux-thunk or a redux-api-middleware). Your state app must be hold by redux store, not in your interface.
You probably have a "top" component will is mount only once, it can dispatch this action (action which is able to get initial state)
I hope it will help

Resources