I have a Setup where i have a map of React Components which needs to shown in view.
Also there are many input select handlers which update the parent ponent state but after the state update the Parent Component render method is called but the Child Component doesn't refresh the Values .
What am i missing here ? I am bit confused
A minimal Reproduction of the Problem Stackblitz.
import React, { PureComponent } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
export const ReactExample = ({ name, value, handleChange }) => (
<select name={name} value={value} onChange={handleChange}>
<option value="A">Apple</option>
<option value="B">Banana</option>
<option value="C">Cranberry</option>
</select>
)
const Section = props => {
return (
<div>
{props.header}
<br />
{props.primaryNode}
<br />
{props.bottom}
</div>
);
};
class App extends React.PureComponent {
state = {
current: 'B'
};
constructor(props) {
super(props);
}
show = [5, 6, 3, 5, 7, 8, 77, 8, 90];
primaryNode = () => {
const filterData = this.show;
return (<div>{filterData}----{this.state.current} // doesn't get updated even though state updates after dropdown</div>);
};
bottom = () => {
return (
<ReactExample name="fruit" value={this.state.current} handleChange={this.handleChange} />
)
}
handleChange = (event) => {
console.log(event.target.value); // always logged and state updated with no change in value
this.setState({ current: event.target.value });
}
data = ["A", "B", "C"];
map = {
"A": (<Section primaryNode={this.primaryNode()} bottom={this.bottom()} header={"A"} />),
"B": (<Section primaryNode={this.primaryNode()} header={"B"} />),
"C": (<Section primaryNode={this.primaryNode()} header={"C"} />)
};
render() {
return (<div>{this.data.map(d => this.map[d])}</div>)
}
}
render(<App />, document.getElementById('root'));
I have a Setup where i have a map of React Components which needs to shown in view.
Also there are many input select handlers which update the parent ponent state but after the state update the Parent Component render method is called but the Child Component doesn't refresh the Values .
What am i missing here ? I am bit confused
A minimal Reproduction of the Problem Stackblitz.
import React, { PureComponent } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
export const ReactExample = ({ name, value, handleChange }) => (
<select name={name} value={value} onChange={handleChange}>
<option value="A">Apple</option>
<option value="B">Banana</option>
<option value="C">Cranberry</option>
</select>
)
const Section = props => {
return (
<div>
{props.header}
<br />
{props.primaryNode}
<br />
{props.bottom}
</div>
);
};
class App extends React.PureComponent {
state = {
current: 'B'
};
constructor(props) {
super(props);
}
show = [5, 6, 3, 5, 7, 8, 77, 8, 90];
primaryNode = () => {
const filterData = this.show;
return (<div>{filterData}----{this.state.current} // doesn't get updated even though state updates after dropdown</div>);
};
bottom = () => {
return (
<ReactExample name="fruit" value={this.state.current} handleChange={this.handleChange} />
)
}
handleChange = (event) => {
console.log(event.target.value); // always logged and state updated with no change in value
this.setState({ current: event.target.value });
}
data = ["A", "B", "C"];
map = {
"A": (<Section primaryNode={this.primaryNode()} bottom={this.bottom()} header={"A"} />),
"B": (<Section primaryNode={this.primaryNode()} header={"B"} />),
"C": (<Section primaryNode={this.primaryNode()} header={"C"} />)
};
render() {
return (<div>{this.data.map(d => this.map[d])}</div>)
}
}
render(<App />, document.getElementById('root'));
Share
Improve this question
asked Nov 17, 2019 at 21:15
Rahul SinghRahul Singh
19.7k13 gold badges68 silver badges94 bronze badges
4 Answers
Reset to default 1The class field map
is initialized only once, at first ponent render. It does not really matter that you change the state, that variable holds the very same data and does not change in time.
One of the solutions would be moving that variable into render:
render() {
const map = {
"A": (<Section primaryNode={this.primaryNode()} bottom={this.bottom()} header={"A"} />),
"B": (<Section primaryNode={this.primaryNode()} header={"B"} />),
"C": (<Section primaryNode={this.primaryNode()} header={"C"} />)
};
return (<div>{this.data.map(d => this.map[d])}</div>)
}
or you could transform map
field into a method:
map = () => ({
"A": (<Section primaryNode={this.primaryNode()} bottom={this.bottom()} header={"A"} />),
"B": (<Section primaryNode={this.primaryNode()} header={"B"} />),
"C": (<Section primaryNode={this.primaryNode()} header={"C"} />)
});
render() {
return (<div>{this.data.map(d => this.map()[d])}</div>)
}
So the data returned by map
is always up-to-date.
I think you're making this more confusing than it needs to be by holding all these values as class properties. The reason that they are not updating is because this.map
is only defined once. You don't need map at all, just render your nodes directly inside render
function which gets called on state changes:
render() {
return (
<div>
<Section primaryNode={this.primaryNode()} bottom={this.bottom()} header={"A"} />
<Section primaryNode={this.primaryNode()} header={"B"} />
<Section primaryNode={this.primaryNode()} header={"C"} />
</div>
)
}
And if you really want it to be a map, then define it inside render
:
render() {
const map = {
"A": <Section primaryNode={this.primaryNode()} bottom={this.bottom()} header={"A"} />,
"B": <Section primaryNode={this.primaryNode()} header={"B"} />,
"C": <Section primaryNode={this.primaryNode()} header={"C"} />
};
return <div>{this.data.map(d => map[d])}</div>
}
Not entirely sure what you're wanting to do, but it sounds like you want your A/B/C values to be updated with state-change. The problem is that you are defining the primaryNode in your map-object before render()
, rather than inside of render()
(which always re-renders when state changes. Try this:
import React, { PureComponent } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
const Section = item => (
<>
<br/>
<section>
{item.header}
<br />
{item.primaryNode}
<br />
{item.bottom}
</section>
</>
);
class App extends React.PureComponent {
constructor(props) {
super(props);
}
state = {
current: 'B'
}
data = ["A", "B", "C"];
show = [5, 6, 3, 5, 7, 8, 77, 8, 90];
bottom = () => (
<select name={"fruit"} value={this.state.current} onChange={this.handleChange}>
<option value="A">Apple</option>
<option value="B">Banana</option>
<option value="C">Cranberry</option>
</select>
);
handleChange = (event) => (
this.setState({ current: event.target.value })
);
primaryNode = () => (
<div className="primary-node">
{this.show} ---- {this.state.current}
</div>
);
render() {
return (
<>
State Value - {this.state.current}
<br/>
<br/>
<br/>
{this.data.map(el => (
<Section
bottom={el === "A" && this.bottom()}
header={el}
primaryNode={this.primaryNode()}
/>
))}
</>
);
}
}
render(<App />, document.getElementById('root'));
Try moving the map
inside the render()
method.
render()
is fired when the state changes so it's the place You're looking for