My question is about life cycle methods in react, now i have this scenario:
Some data are fetched by ajax in componentDidMount in the parent component and then this data passed as a prop to as the child component, now we are in the child so we can implement different life cycle methods,
componentDidMount: can not see the true value of the prop for example logging date in it returns empty array instead of the desired array
componentWillMount: the same.
componentWillReceiveProps: the same if not use nextProps
componentDidUpdate: logging data returns the desired data passed as prop
render: returns the desired data
So should i always be forced to call my data in componentDidUpdate or render???, Is there a way to manage that without redux??
Your child's componentWillMount and componentDidMount may only be receiving an empty array (I assume this is a default) because the Child component is instantiated before the API result is returned e.g. <Child result={result} /> as opposed to result.length > 0 && <Child result={result} />. Mount only happens once and then componentDidUpdate happens after for every prop change.
Assuming that you are just displaying the data passed down, it is perfectly acceptable to handle it in render. Otherwise if you want to perform additional side-effects then check for it's completeness in the parent component (above paragraph) at which point you should be able to see the full value in the child's componentDidMount. Alternatively you can perform the completeness check in the child's componentDidUpdate as you suggest and handle it there instead.
Related
right now im have tree component like this
<UserOrderApproval>
<Table>
<TableRow>
<ComponentList />
</ChildrenProps1>
</Parent>
</UserOrderApproval>
im take props from redux in UserOrderApproval component, and passing that props to every children component. The problem, when children component make some change in redux, the state from UserOrderApproval component will change & make all children component re render & make component life cycle run again. How to do prevent this?
How best practice to passing props from parent to nested children like this?
Many Thanks
As KuchBhi said, you'll need to use the shouldComponentUpdate lifecycle method.
shouldComponentUpdate is called any time when new props or state are passed to the component and by default returns true (might only return true for updated props, not 100% sure). The method accepts two parameters: nextProps and nextState. You can then compare these to this.props and this.state to decide whether you should update or not.
The default/typical implementation of this method is something along the lines of this (pseudo):
shouldComponentUpdate(nextProps, nextState) {
//Really it's a deeper equality check, not just the props/state object
return this.props !== nextProps || this.state !== nextState;
}
What you can do as an alternative is build this out a bit more with cases where false is returned. Here is an example from one of my projects where I only want to rerender when two specific props are different:
shouldComponentUpdate(nextProps) {
const minUpdated = this.props.min !== nextProps.min;
const maxUpdated = this.props.max !== nextProps.max;
return minUpdated || maxUpdated;
}
The component also has the props such as scale, align, width, etc. but I don't want to update when I receive those as new props so I don't check to see if they're not equal.
You could essentially just return false in this method and never update after the initial load, but I would avoid that as it defeats the purpose of react.
edit: As varoons said, this is likely a great scenario for you to use the new context API and I highly recommend looking into it, but it is useful to know about component lifecycle methods as well.
Use shouldComponentUpdate(), pure components, or connect redux to only the child components that need them.
https://reactjs.org/docs/react-component.html#shouldcomponentupdate
https://reactjs.org/docs/react-api.html#reactpurecomponent
This is a good scenario to use React's context api as this seems like a prop drilling case - You are passing data down to components that do not use it.
You could make UserOrderApproval(assuming it is the parent connected to the global state/redux) the context provider and ComponentList the consumer. This way only the components handling/using the data will re-render.
https://reactjs.org/docs/context.html
https://scotch.io/tutorials/get-to-know-reacts-new-context-api
What exactly componentWillReceiveProps and getDerivedStateFromProps are subtle question for me. Because, I just came across to an issue while using getDerivedStateFromProps:
// Component
state = {
myState: []
}
// Using this method works fine:
componentWillReceiveProps(nextProps) {
this.setState({
myState: nextProps.myPropsState
})
}
// But using this method will cause the checkboxes to be readonly:
static getDerivedStateFromProps(nextProps,prevProps) {
const { myPropsState: myState } = nextProps
return {
myState
}
}
// And here's checkbox
<input type="checkbox" id={`someid`}
onChange={(e) => this.handleMethod(e, comp.myState)}
checked={myState.indexOf(comp.myState) > -1} />
React version: 16.4.1
getDerivedStateFromProps is not a direct alternative to componentWillReceiveProps, purely because of the fact that its called after every update, whether its the change in state or change in props or re-render of parent.
However whatever is the case, simply returning the state from getDerivedStateFromProps is not the right way, you need to compare the state and props before returning the value. Else with every update the state is getting reset to props and the cycle continues
As per the docs
getDerivedStateFromProps is invoked right before calling the render
method, both on the initial mount and on subsequent updates. It should
return an object to update the state, or null to update nothing.
This method exists for rare use cases where the state depends on
changes in props over time. For example, it might be handy for
implementing a <Transition> component that compares its previous and
next children to decide which of them to animate in and out.
Deriving state leads to verbose code and makes your components
difficult to think about. Make sure you’re familiar with simpler
alternatives:
If you need to perform a side effect (for example, data fetching
or an animation) in response to a change in props, use
componentDidUpdate lifecycle instead.
If you want to re-compute some data only when a prop changes, use
a memoization helper instead.
If you want to “reset” some state when a prop changes, consider
either making a component fully controlled or fully uncontrolled
with a key instead.
P.S. Note that the arguments to getDerivedStateFromProps are props and state and not nextProps and prevProps
To get into more details,
In order to make changes based on props change, we need to store prevPropsState in state, in order to detect changes. A typical implementation would look like
static getDerivedStateFromProps(props, state) {
// Note we need to store prevPropsState to detect changes.
if (
props.myPropsState !== state.prevPropsState
) {
return {
prevPropsState: state.myState,
myState: props.myPropsState
};
}
return null;
}
Finally, I resolved my issue. It was a painful debugging:
// Child Component
// instead of this
// this.props.onMyDisptach([...myPropsState])
// dispatching true value since myPropsState contains only numbers
this.props.onMyDispatch([...myPropsState, true])
This is because, I have two conditions: 1) on checkbox change (component) 2) on reset button pressed (child component)
I was needing to reset the states when reset button is pressed. So, while dispatching state to the props for reset button, I used a boolean value to know it's a change from the reset. You may use anything you like but need to track that.
Now, here in the component, I found some hints to the differences between componentWillReceiveProps and getDerivedStateFromProps after debugging the console output.
// Component
static getDerivedStateFromProps(props, state) {
const { myPropsState: myState } = props
// if reset button is pressed
const true_myState = myState.some(id=>id===true)
// need to remove true value in the store
const filtered_myState = myState.filter(id=>id!==true)
if(true_myState) {
// we need to dispatch the changes to apply on its child component
// before we return the correct state
props.onMyDispatch([...filtered_myState])
return {
myState: filtered_myState
}
}
// obviously, we need to return null if no condition matches
return null
}
Here's what I found the results of the console output:
getDerivedStateFromProps logs immediately whenever props changes
componentWillReceiveProps logs only after child propagates props changes
getDerivedStateFromProps doesn't respond to the props changes ( I meant for the dispatch changes as in the example code)
componentWillReceiveProps responds to the props changes
Thus, we needed to supply the changes to child component while using getDerivedStateFromProps.
The process of pasting true value in the state I require because getDerivedStateFromProps handle all the changes unlike componentWillReceiveProps handles only the child component dispatches the changes to the props.
By the way, you may use custom property to check if it is changed and update the value if getDerivedStateFromProps but for some reason I have to tweak this technique.
There might be some confusion on my wording but I hope you'll get it.
From the react docs:
Note that this method is fired on every render, regardless of the cause. This is in contrast to UNSAFE_componentWillReceiveProps, which only fires when the parent causes a re-render and not as a result of a local setState.
You are effectively overriding your state with the current props every time after calling setState(). So when you check a box (e) => this.handleMethod(e, comp.myState) is called which is assume calls setState() to update the checked state of the checkbox. But after that getDerivedStateFromProps() will be called (before render) that reverts that change. This is why unconditionally updating state from props is considered an anti-pattern.
Need help with enzyme for testing. I have checked here but it did not help: How does one access state on a nested React component wrapped by an HOC?
Here is my problem:
How can I check function called or not and state of Child component? I also need to check lifecycle, so I used mount of parent component, but haven't done that yet.
I used below when I rendered one component:
expect(searchWrapper.find('select [selected]').val()).to.equal('someId'),
but with mount, this is not working, as it complains searchWrapper.find(...).val is not a function which I believe is a cheer IO and it worked with render only. So what will be the proper way for me?
What I intend to do: On child select change, check (and/or match):
child func is called once
parent func is called once (called inside 1.)
parent state
child state
lifecyle methods like componentWillReceiveProps and componentDidMount etc
Result rendered output validation, also including some of the above.
Component is like
class ParentWrapper {
// state and other functions and lifecycle methods
render(){
....
<SearchWrapper {...props}/>
<ResultWrapper {...props}/>
}
}
class SearchWrapper {
// state
// lifecycle methods
// functions
}
and same for ResultWrapper.
Summarizing it again with some code:
I want to access state and other objects of Child node, just like we can do with root. Also access rendered objects like selected option from the select menu, for example.
// state is not a function, as it has to be called for root only, then whats the alternative
expect(parentWrapper.find(SearchWrapper).state().searchId).to.equal('someValue');
// Below also did not work, it worked when I tested SearchWrapper individually using render()
// which is a CheerIO function, but I need to use mount of parent for whole component testing
// and inter-component interactions
searchWrapper.find('.form1 select').simulate('change', {target : {value : 'someValue'}});
// sinon spy on change is triggered
expect(searchWrapper.render().find('select [selected]').val()).to.equal('someValue');
// AssertionError: expected undefined to equal 'someValue'
In my react js app, I can't seem to use the state value as props for the child component.
In the parent component, constructor, the app has a null state called selectedWarehouseID.
This state(selectedWarehouseID) should update with some information in the componentWillMount() method.
Now, in the render method of the parent component I am embedding another child component that has this state as a props.
<ItemChooser
productsByWarehouse = { this.state.selectedWarehouseID }
/>
Now here's the problem, in the child component, the value of this.props.productsByWarehouse is always null. I found a nice explanation of why this happens, but how to wait for the data that updates in the parents componentWillMount() to access the updated value of the state that being passed in the child component props?
Possible solutions are:
1. You are storing the props value in state of child component so, Update the state of child component whenever any change happen to props values (state of parent). For that use componentWillReceiveProps lifecycle method.
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.
Like this:
componentWillReceiveProps(newProps){
this.setState({
value: newProps.productsByWarehouse
});
}
Note: Replace value by the actual key in which you are storing the value.
2. Don't store the props values in state of child component, directly use this.props.productsByWarehouse, Whenever you change the parent state values, child will automatically get the updated value in props.
All props form a one-way-down binding between the child property and
the parent one: when the parent property updates, it will flow down to
the child, but not the other way around. This prevents child
components from accidentally mutating the parent’s state, which can
make your app’s data flow harder to reason about. In addition, every
time the parent component is updated, all props in the child component
will be refreshed with the latest value. - One-Way Data Flow
The Vue2 Component Docs suggests doing the following to use props as an initial value:
// via https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
So in my code I mimicked those instructions here.
However data() in Note.vue isn't being updated even though the prop value is received according to vue-devtools.
Haven't had success setting the values with the mounted or created lifescyle methods.
When I use static data, this seems to work fine, how can I ensure the child component reacts to receiving props when it comes from a remote source?
When you are passing initialNote as prop for initial value, but I see initialNote is being populated asynchronously in getNote method, so it will not be present initially when the component will be mounted. It will be populated after some time by the time initialisation would have already happened.
In the example give in vue documentation, initialCounter is static value which will perfect as it will have same value from beginning.