i have a chart component and i'm fetching data from an api through the props data to put in chart but when i'm sending the parent to chart component as props it's not updating the parent state is updated but the child props are not updating and if i log it in
componentWillReceiveProps(props) {
console.log(props);
// it shows updated value but if i do this
this.loadData();
}
loadData = () => {
console.log(props)
// it's value is old and not updated
}
i have check it in parent the state is updated is and i have a click event in parent when i first click it the parent state changes but chart component props will not change and if i click second time chart component props will the state of first time click state
here's my github repo https://github.com/naveenkash/forex
look in chart component props and pages index.js chart in getting props through index.js
because in componentWillReceiveProps props is reference to new props, they are not yet available in other parts of class, so you should pass them to loadData
componentWillReceiveProps(props){
this.loadData(props);
}
loadData=(props)=>{
console.log(props)
}
What Dmitry said is accurate, this.props hasn't changed as of componentWillReceiveProps(props). Therefore when you call this.loadData() you're still calling it with the old props.
Note that componentWillReceiveProps(props) is
unsafe
You should use componentDidUpdate(previousProps), where you can call this.loadData() and it will execute with updated this.props.
It's js scope issue not react event lift cycle issue, considering changing the code as below:
componentWillReceiveProps(props) {
console.log(props);
// it shows updated value but if i do this
this.loadData(props);
}
loadData = (newProps) => {
console.log(newProps)
// it's value is old and not updated
}
Related
I have a parent Component with a state variable that gets changed by one of its child components upon interaction. The parent then also contains some more components based on the data in the state variable.
The problem is that the child component rerenders when the state of its parent changes because the reference to the setState function changes. But when I use useCallback (as suggested here), the state of my parent just does not update at all.
This is my current setup:
function ArtistGraphContainer() {
const [artistPopUps, setArtistPopUps] = useState([])
const addArtistPopUp = useCallback(
(artistGeniusId, xPos, yPos) => {
setArtistPopUps([{artistGeniusId, xPos, yPos}].concat(artistPopUps))
},
[],
)
return (
<div className='artist-graph-container'>
<ArtistGraph addArtistPopUp={addArtistPopUp} key={1}></ArtistGraph>
{artistPopUps.map((popUp) => {
<ArtistPopUp
artistGeniusId={popUp.artistGeniusId}
xPos={popUp.xPos}
yPos={popUp.yPos}
></ArtistPopUp>
})}
</div>
)
}
And the Child Component:
function ArtistGraph({addArtistPopUp}) {
// querying data
if(records) {
// wrangling data
const events = {
doubleClick: function(event) {
handleNodeClick(event)
}
}
return (
<div className='artist-graph'>
<Graph
graph={graph}
options={options}
events={events}
key={uniqueId()}
>
</Graph>
</div>
)
}
else{
return(<CircularProgress></CircularProgress>)
}
}
function areEqual(prevProps, nextProps) {
return true
}
export default React.memo(ArtistGraph, areEqual)
In any other case the rerendering of the Child component wouldn't be such a problem but sadly it causes the Graph to redraw.
So how do I manage to update the state of my parent Component without the Graph being redrawn?
Thanks in advance!
A few things, the child may be rerendering, but it's not for your stated reason. setState functions are guaranteed in their identity, they don't change just because of a rerender. That's why it's safe to exclude them from dependency arrays in useEffect, useMemo, and useCallback. If you want further evidence of this, you can check out this sandbox I set up: https://codesandbox.io/s/funny-carson-sip5x
In my example, you'll see that the parent components state is changed when you click the child's button, but that the console log that would fire if the child was rerendering is not logging.
Given the above, I'd back away from the usCallback approach you are using now. I'd say it's anti-pattern. As a word of warning though, your useCallback was missing a required dependency, artistPopUp.
From there it is hard to say what is causing your component to rerender because your examples are missing key information like where the graphs, options, or records values are coming from. One thing that could lead to unexpected rerenders is if you are causing full mounts and dismounts of the parent or child component at some point.
A last note, you definitely do not need to pass that second argument to React.memo.
I'm fetching data for GameChart from api and change the redux state. Inside GameChart.jsx file I drawing chart when componentDidUpdate called. But redux state change sometimes don't call componentDidUpdate.
Console logs of GameChart.jsx lifecycles
GameChart.jsx Console logs, when componentDidUpdate didn't called (it happens magically with 50/50 chance...):
Chart mapStateToProps
Chart componentDidMount
Chart mapStateToProps
GameChart.jsx Console logs, when all is OK, and componentDidUpdate called, when redux state changed:
Chart mapStateToProps
Chart componentDidMount
Chart mapStateToProps
Chart mapStateToProps
Chart componentDidUpdate
Chart mapStateToProps
Here is very schematic code:
GameChart.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
class GameChart extends Component {
...
componentDidMount() { console.log("Chart componentDidMount") }
componentDidUpdate() {
console.log("Chart componentDidUpdate");
/* When socket.js call initGame, and redux update state, I need to
render canvas from this state inside GameChart.jsx */
... Drawing chart, using data from this.props.game ...
}
render() {
return (
<canvas ref={...} ... />
);
}
}
const mapStateToProps = state => {
console.log('Chart mapStateToProps');
return { game: state.game }
};
export default connect(mapStateToProps)(GameChart);
gameReducer.js. Action, which initGame called
case INIT_GAME:
return {
status: action.payload.roll !== null ? "rolling" : "betting",
...action.payload
}
socket.js
/* When websocket connected, it sends current game state to client.
Client need to write this state in redux state and re-render
GameChart.jsx, using this new rewrited by dispatch action state */
socket.onmessage = (msg) => {
store.dispatch(initGame)(msg);
}
I checked redux-dev-tools. Diff tab displays, that game inited successfully and state changed to what websocket gave. I checked out for redux state mutations, there is no state mutations. When refreshing page, sometimes magically componentDidUpdate didn't called, but sometimes it called. It could call arbitrarily with the chance of 50/50.
mapStateToProps connects your redux store to your component's props. The component doesn't rerender when your props are updated(unless the props belong to the state of the parent's state).
componentDidUpdate is called when your component's state is updated. A change of props will not affect this.
In short, the props from redux store don't follow the lifecycle hooks of your component.
You will probably need to trigger a rerender your component from where you are calling a dispatch for the redux state.
mapStateToProps is called after dispatch has run.
Then Redux does check if result returned is referentially different from previous one. Only if there are differences component is provided with new props. That runs componentDidUpdate and render.
So you either have referentially same data returned and it's legit reason to miss updating component(and save some time for better performance).
Or your reducer is mutating state(that's no-no for Redux) and you should fix that.
how to force render our component when props changes?
how to force render parent component when child component's props changes?
i searched a lot but all solutions are deprecated in new React version.
in my any pages (Route exact path="/post/:id" component={post}) for example (siteUrl/post/1) i am getting (:id) from props (this.props.match.params) and that works.
but when i am in single page component (Post) and this Route (siteUrl/post/1) when i am passing new Props to this component (:id).
props will changes but single component and parent component Will not re render...
You may be using componentDidMount method.
componentDidMount() is invoked immediately after a component is
mounted (inserted into the tree). Initialization that requires DOM
nodes should go here. If you need to load data from a remote endpoint,
this is a good place to instantiate the network request.
but you need to use componentDidUpdate.
componentDidUpdate() is invoked immediately after updating occurs.
This method is not called for the initial render.
You can also use state and other React features without writing a class.
read more: https://reactjs.org/docs/hooks-effect.html
To make both parent and child re-render you need to path prop from parent to it's child.
// parent using
<Parent someProp={{someVal}} />
// parent render:
render() {
const { someProp } = this.props
<Child someProp={{someProp}} />
}
this will surely re-render both components, unless you stated another logic in componentShouldUpdate
in your case Router looks like a parent for Parent so you should only path :id as a prop.
Make sure Router is at the top level, right under the App
Important is ,that you initialise the someVal of the child first in the constructor
public static getDerivedStateFromProps(
nextProps,
nextState
) {
let { someVal } = nextProps;
if (nextState.someVal !== someVal) {
return {
initialVal,
someVal: someVal
};
}
return null;
}
After it will rerender on prop changes because the state changes
I want to update my local state if the props were changed. But I could not find a suitable solution to this problem. getDerivedStateFromProps gives me only nextProps and prevState but I need both prevProps and nextProps. Please write any your solution that might solve this problem)
Situation: The Cart component (stateful) gets the fetched data from the props and puts that data in this.state.cart. The Cart component has counterHandler method that triggers whenever the counter is changed (onIcrease, onDecrease, onChange). This handler calculates the data and makes a new data based on the event ( calculating totalPrice and updating the amount of the item ). Then puts the new data into setState to update the UI. The state doesn't change because I do this:
static getDerivedStateFromProps(nextProps, prevState) {
if (!_.isEqual(nextProps.cart, prevState.cart)) {
return { cart: nextProps.cart };
} else return null;
}
I do this because I want to update the state when the real data is changed ( the data that comes from the props ).
Goal: updating the counter's and the total price's value without making any-side effect ( the values might be updated in the store or only for the local state ).
This is what I mean:
You might want to use componentDidUpdate which is called when the props on the component update. It receives prevProps and prevState as parameters, and you have access to "nextProps" as this.props.
For example
componentDidUpdate(prevProps, prevState) {
if (!_.isEqual(this.props.cart, prevState.cart)) {
this.setState ({ cart: this.props.cart });
}
}
You can use componentWillReceiveProps method of react class. This method will be executed when parent component will render, its child will execute componentWillReceiveProps method in child. In this method in child you can update state based on new props.
componentWillReceiveProps(nextProps){
//You can access current props via this.props
//You can access newProps from argument
//Change component state after comparing both props
}
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.