最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Map of React Components not re rendering on State Change - Stack Overflow

programmeradmin5浏览0评论

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
Add a ment  | 

4 Answers 4

Reset to default 1

The 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

发布评论

评论列表(0)

  1. 暂无评论