Method called after latest render - reactjs

In my reactjs application, in one component, render function is called many times. I have some data available only at the last time the render is called. I need to do some treatments on this data. Where can I do it?
componentWillMount() {
get(this.props, 'myarraydata', []).forEach(element => {
//here i will do some treatments for each elemment
});
}
render() {
console.log(get(this.props, 'myarraydata', []));
return (
<div>
<pre>{JSON.stringify(get(this.props, 'myarraydata', []), null, 2) }</pre>
</div>
);
}
As you can see, in my application, this render method is called many times, and it uses myarraydata passed from another component and myarraydata is available only when render is called for the last time (means when render is called for the first, second, .. times, my arraydata is empty). but the problem is that componentwillmount method is called only one time, and at that time, myarraydata still empty and i can't do the treatments i need

componentDidUpdate lifecycle hook is called after each subsequent render, as it can be seen on this diagram:
otice that it isn't called on initial render when componentDidMount is called instead after render hook.
Data that was received via props can be processed there, this requires to call setState or forceUpdate then to make it rendered.
In original code, this requires to introduce the state to this component. Also, componentWillMount was deprecated because it was commonly misused for cases like this one. It should be replaced with constructor code or componentDidMount. In this case the state synchronously derives from myarraydata prop, this is what getDerivedStateFromProps is for:
state = { processedData: null };
static getDerivedStateFromProps(props, state) {
if (conditionsToNotProcessData) return null;
const processedData = props.myarraydata || [];
for (const item of processedData) { ... }
return { ...state, processedData };
}
render() {
return (
<div>
<pre>{JSON.stringify(this.state.processedData)}</pre>
</div>
);
}

Try using the componentDidMount lifecycle method.
Need elaboration on
I have some data available only at the last time the render is called
What does this mean exactly?
EDIT : Try this :
componentWillMount() {
let data = get(this.props, 'myarraydata', [])
if(data.length !== 0){
data.forEach(element => {
//here i will do some treatments for each elemment
});
}
}

Related

ReactJS fetch() after component mounted, on change

Most examples shows ReactJS fetch() in componentDidMount. In my example, I need a fetch as part of a state change received from another component, after initialization (initially my component is empty). I currently do it in render() which is always called on a state change.
But something's wrong: the Fetch works and the render() is called on the state change, but return <div>..</div> doesn't output, so I don't see my rendered HTML.
Component
componentDidMount() {
/* Nothing here. The component should initially be empty. */
}
render() {
const { study, items } = this.props;
// THIS WORKS Another component triggers this one's state change, I get the alert
alert('State changed: ' + study);
if (study) {
/* Ajax call in render(). The fetch works as verified with console.log(result) */
fetch("/api/myCall?id=" + study)
.then(res => res.json())
.then(
(result) => {
return <div>RESULT OBTAINED</div> <-- THIS DOESN'T WORK, result is OK
},
(error) => {
return <div>ERROR</div>
}
);
} else {
return <div>EMPTY</div>
}
For this to work you would have to block the rendering until the fetch is fullfilled, or manipulate the state of your component depending on the result/error, which in turn would cause another render and with it another fetch call so you would get an endless loop. So calling fetch directly inside render is never a good idea if your UI depends on the result.
Instead, use componentDidUpdate(), like this:
componentDidUpdate(prevProps) {
const { study } = this.props;
if(prevProps.study !== study){
fetch("/api/myCall?id=" + study).then(result => {
//Manipulate your state here!
})
}
}
This way you only request new data and update your components state when the study property, which is passed from outside, changes. No need to call fetch inside of render.
-- EDIT --
Alternatively you could also do the fetch in the other component and pass all the data you need via props to your component. That way you would have a stateless component which is easier to maintain.

Where is the best place to make calculations outside the render method in React?

I have a render method in my container component like this:
render() {
const { validationErrors } = this.state
const { errorsText, errorsFields} = validationErrors.reduce(
(acc, error) => {
acc.errorsText.push(error.text)
acc.errorsFields[error.field.toLowerCase()] = true
return acc
},
{
errorsText: [],
errorsFields: {},
},
)
return (
<MyViewComponent
errorsText={errorsText}
errorsFields={errorsFields}
/>
)
}
As you can see every render there are some computations happens (returned array and object with the new values), then I pass it into my child component as a props. I have a feeling that this is a wrong pattern. We should keep render function 'pure'. Isn't it? The question is: Where is the best place for making such computations outside the render?
If this were a functional component (which I highly recommend you use in the future, by the way), you'd be able to use the 'hook' useEffect to recalculate errorsText and errorsField whenever this.state.validationErrors changes, and only when it changes.
For your Class Component, however, I assume at some point you set this.state.validationErrors. What you should do is create a method that runs your reducer and stores errorsText and errorsField to state, then place a call to this method after each point you set this.state.validationErrors. Then, remove the logic in the render method and replace errorsText and errorsField with this.state.errorsText and this.state.errorsField respectively.
Doing this will ensure you only ever run your reducer when necessary (i.e. when this.state.validationErrors changes).
Your component would end up looking something like this:
class MyComponent extends Component {
...
someCallback() {
const validationErrors = someFunctionThatReturnsErrors();
// We do the logic here, because we know that validationErrors
// could have changed value
const { errorsText, errorsFields } = validationErrors.reduce(
(acc, error) => {
acc.errorsText.push(error.text);
acc.errorsFields[error.field.toLowerCase()] = true;
return acc;
}, {
errorsText: [],
errorsFields: {},
},
);
// Put everything in the state
this.setState({
validationErrors, // you may not even need to set this if it's not used elsewhere`
errorsText,
errorsFields
});
}
...
render() {
const {
errorsText,
errorsFields
} = this.state;
return (
<MyViewComponent
errorsText={errorsText}
errorsFields={errorsFields}
/>
);
}
}
It is pure, as it has no side effects.
As long as this does not create performance issues I see no problem with this. If it does create performance issues, you should look into memoizing the reduce. If you were using hooks you could use the built-in React.useMemo for this. While using class version you could look into something like https://www.npmjs.com/package/memoize-one

Where to move setState() callback from deprecated componentWillReceiveProps()?

React 16 deprecates componentWillReceiveProps() lifecycle method. The preferred replacement is new getDerivedStateFromProps() or componentDidUpdate(). Now let's suppose I have some code like that:
componentWillReceiveProps(newProps) {
if (this.props.foo !== newProps.foo) {
this.setState({foo: newProps.foo}, () => this.handleNewFoo()});
}
}
I could try moving the code to getDerivedStateFromProps() like that:
getDerivedStateFromProps(newProps, prevState) {
if (this.props.foo !== newProps.foo) {
return {foo: newProps.foo};
} else {
return null;
}
}
but what do I do with the callback from setState()? Is moving it to componentDidUpdate the only option? I'd rather have this.handleNewFoo() invoked before render() to avoid visual and performance cost of two renders.
If you haven't read this blog post you should
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#when-to-use-derived-state
If you are simply assigning props.foo to state.foo without any calculation / processing then you need to ask yourself should you really need that field in state. Because you can always use this.props.foo instead of this.state.foo.
what is this.handleNewFoo method does?
are you doing another setState inside handleNewFoo? if you are then you are triggering multiple render calls.
eg.
this.setState({foo: this.props.foo} // one render call, () => this.handleNewFoo())
const handleNewFoo = () => {
this.setState({ }) // another render call
}
If you are not doing any setState inside handleNewFoo then ask yourself does handleNewFoo has to be called on prop change?
If you are using setState inside the handleFoo i would suggest something like this
getDerivedStateFromProps(newProps, prevState) {
if (this.props.foo !== newProps.foo) {
return this.handleNewFoo(this.props.foo, prevState);
} else {
return prevState;
}
}
handleNewFoo(foo, state) {
// do some calculation using foo
const derivedFoo = state.foo + foo;
// if your are using immutability_helper library.
const derivedState = update(state, { foo: {$set: derivedFoo}});
return derivedState
}
It all depends on what you are doing inside this.handleNewFoo
EDIT:
Above code won't work as #Darko mentioned getDerivedStateFromProps is static method and you cannot access this inside it. And you cannot get the previous props inside getDerivedStateFromPropslink. So this answer is completely wrong.
one solution is to use useEffect
useEffect(() => {
setState(...)
}, [props.foo])
this useEffect will call whenever the props.foo is changed. And also the component will be rendered twice one for prop change and another for setState inside useEffect. Just be careful with setState inside useEffect as it can cause infinite loop.

componentWillReceiveProps seems to be called twice

In my react project, the componentWillReceiveProps() function seems to be called twice, but not sure what the problem is.
Here is the code.
import React, { Component, PropTypes } from 'react';
...
class MessagesList extends Component {
constructor(props) {
super(props);
this.state = {
message: '',
messages: []
};
...
componentWillMount() {
this.props.init_message();
};
componentWillReceiveProps(nextProps) {
this.setState({
messages: this.props.user.messages
});
var msgs = this.props.user.messages;
var total_group = [];
var msg_group = [];
var group_date = 0;
setTimeout(() => {
if (typeof msgs != 'undefined') {
for(var i = 0; i < msgs.length; i++) {
...
}
}
}, 100);
};
render() {
return (
<div className="row">
<div className="col-12">
<div className="messages">
{this.state.messages.map(message => {
return (
<div>{message.user}: {message.message} : {message.date}</div>
)
})}
</div>
</div>
...
</div>
);
}
}
I was going to read the msgs.length in the componentWillReceiveProps(), I got the following issue.
msgs.length is undefiend
After that I got the values of array, so I think the componentWillReceiveProps() seems to be called twice. So in the first call, can't read the value and then in the second call, read the value at least.
Please help me.
componentWillReceiveProps is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. Make sure to compare the current and next values if you only want to handle changes.
You will get the details from react docs.
ComponentWillReceiveProps is a method from the growth/update phase of the React lifecycle.
The Growth phase is triggered in three different ways: changing of props, changing of state or calling forceUpdate().
The value you are referring to in componentWillReceiveProps, this.props.user.messages, is the current value not the nextProps value.
Also something to consider is that the setState method is actually an asynchronous function. So when that setting of state takes place, it will cause another rerender.
I suspect, but I cannot be sure without more of your code, that setState is called once with your original value from props which triggers another update cycle. During this next update cycle the setState method now sets state to the new prop values.
Are you perhaps meaning to use nextProps.user.messages instead of this.props.user.messages?

Re-render React component when prop changes

I'm trying to separate a presentational component from a container component. I have a SitesTable and a SitesTableContainer. The container is responsible for triggering redux actions to fetch the appropriate sites based on the current user.
The problem is the current user is fetched asynchronously, after the container component gets rendered initially. This means that the container component doesn't know that it needs to re-execute the code in its componentDidMount function which would update the data to send to the SitesTable. I think I need to re-render the container component when one of its props(user) changes. How do I do this correctly?
class SitesTableContainer extends React.Component {
static get propTypes() {
return {
sites: React.PropTypes.object,
user: React.PropTypes.object,
isManager: React.PropTypes.boolean
}
}
componentDidMount() {
if (this.props.isManager) {
this.props.dispatch(actions.fetchAllSites())
} else {
const currentUserId = this.props.user.get('id')
this.props.dispatch(actions.fetchUsersSites(currentUserId))
}
}
render() {
return <SitesTable sites={this.props.sites}/>
}
}
function mapStateToProps(state) {
const user = userUtils.getCurrentUser(state)
return {
sites: state.get('sites'),
user,
isManager: userUtils.isManager(user)
}
}
export default connect(mapStateToProps)(SitesTableContainer);
You have to add a condition in your componentDidUpdate method.
The example is using fast-deep-equal to compare the objects.
import equal from 'fast-deep-equal'
...
constructor(){
this.updateUser = this.updateUser.bind(this);
}
componentDidMount() {
this.updateUser();
}
componentDidUpdate(prevProps) {
if(!equal(this.props.user, prevProps.user)) // Check if it's a new user, you can also use some unique property, like the ID (this.props.user.id !== prevProps.user.id)
{
this.updateUser();
}
}
updateUser() {
if (this.props.isManager) {
this.props.dispatch(actions.fetchAllSites())
} else {
const currentUserId = this.props.user.get('id')
this.props.dispatch(actions.fetchUsersSites(currentUserId))
}
}
Using Hooks (React 16.8.0+)
import React, { useEffect } from 'react';
const SitesTableContainer = ({
user,
isManager,
dispatch,
sites,
}) => {
useEffect(() => {
if(isManager) {
dispatch(actions.fetchAllSites())
} else {
const currentUserId = user.get('id')
dispatch(actions.fetchUsersSites(currentUserId))
}
}, [user]);
return (
return <SitesTable sites={sites}/>
)
}
If the prop you are comparing is an object or an array, you should use useDeepCompareEffect instead of useEffect.
componentWillReceiveProps() is going to be deprecated in the future due to bugs and inconsistencies. An alternative solution for re-rendering a component on props change is to use componentDidUpdate() and shouldComponentUpdate().
componentDidUpdate() is called whenever the component updates AND if shouldComponentUpdate() returns true (If shouldComponentUpdate() is not defined it returns true by default).
shouldComponentUpdate(nextProps){
return nextProps.changedProp !== this.state.changedProp;
}
componentDidUpdate(props){
// Desired operations: ex setting state
}
This same behavior can be accomplished using only the componentDidUpdate() method by including the conditional statement inside of it.
componentDidUpdate(prevProps){
if(prevProps.changedProp !== this.props.changedProp){
this.setState({
changedProp: this.props.changedProp
});
}
}
If one attempts to set the state without a conditional or without defining shouldComponentUpdate() the component will infinitely re-render
You could use KEY unique key (combination of the data) that changes with props, and that component will be rerendered with updated props.
componentWillReceiveProps(nextProps) { // your code here}
I think that is the event you need. componentWillReceiveProps triggers whenever your component receive something through props. From there you can have your checking then do whatever you want to do.
I would recommend having a look at this answer of mine, and see if it is relevant to what you are doing. If I understand your real problem, it's that your just not using your async action correctly and updating the redux "store", which will automatically update your component with it's new props.
This section of your code:
componentDidMount() {
if (this.props.isManager) {
this.props.dispatch(actions.fetchAllSites())
} else {
const currentUserId = this.props.user.get('id')
this.props.dispatch(actions.fetchUsersSites(currentUserId))
}
}
Should not be triggering in a component, it should be handled after executing your first request.
Have a look at this example from redux-thunk:
function makeASandwichWithSecretSauce(forPerson) {
// Invert control!
// Return a function that accepts `dispatch` so we can dispatch later.
// Thunk middleware knows how to turn thunk async actions into actions.
return function (dispatch) {
return fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
);
};
}
You don't necessarily have to use redux-thunk, but it will help you reason about scenarios like this and write code to match.
A friendly method to use is the following, once prop updates it will automatically rerender component:
render {
let textWhenComponentUpdate = this.props.text
return (
<View>
<Text>{textWhenComponentUpdate}</Text>
</View>
)
}
You could use the getDerivedStateFromProps() lifecyle method in the component that you want to be re-rendered, to set it's state based on an incoming change to the props passed to the component. Updating the state will cause a re-render. It works like this:
static getDerivedStateFromProps(nextProps, prevState) {
return { myStateProperty: nextProps.myProp};
}
This will set the value for myStateProperty in the component state to the value of myProp, and the component will re-render.
Make sure you understand potential implications of using this approach. In particular, you need to avoid overwriting the state of your component unintentionally because the props were updated in the parent component unexpectedly. You can perform checking logic if required by comparing the existing state (represented by prevState), to any incoming props value(s).
Only use an updated prop to update the state in cases where the value from props is the source of truth for the state value. If that's the case, there may also be a simpler way to achieve what you need. See - You Probably Don't Need Derived State – React Blog.

Resources