简体   繁体   中英

React component with D3 chart renders too many times

I have this component:

export default function ThirdSlide({ userTopArtistsAllTime }) {
  const newref = useRef();

  const { items } = userTopArtistsAllTime;

  let genresObj = {};
  let genres = [];

  useEffect(() => {
    getData();
    createPie(genresObj);
  }, [genresObj]);

  const getData = () => {
    items
      ? items.map((item) => {
          genres.push(item.genres[0]);
          genres.push(item.genres[1]);
        })
      : console.log("null");

    if (genres.length > 1) {
      for (let value in genres) {
        let key = genres[value];
        genresObj[key] ? genresObj[key]++ : (genresObj[key] = 1);
      }
    }
  };

  const createPie = (data) => {
    ....
  };

  return (
    <React.Fragment>
      <Background>
        <div>
          <span>What genres do you favorite artists fit in?</span>
        </div>
        <div
          style={{ marginLeft: "50px", marginTop: "200px" }}
          ref={newref}
        ></div>
      </Background>
    </React.Fragment>
  );
}

The createPie(data) function just creates a chart in D3 and its working okay. The problem that I have is that react renders the SVG 5 times instead of just once and I think it has to do with useEffect and my props?

How can I restructure this code so createPie is called just once?

Because useEffect dependency is genresObj and in every render a new empty object is created by let genresObj = {}; so useEffect run in every render.

You can depend useEffect on items and move variables inside useEffect scope.

  useEffect(() => {
    let genresObj = {};
    let genres = [];
    getData();
    createPie(genresObj);
  }, [items]);

However, for code actually works you need pass down parameters to getData as well.

Adding more color to Makan's answer.

useEffect will rerun the effect every time any one of its dependencies changes . To know if a dependency changes, useEffect uses === to determine whether two consecutive dependencies are equal.

In JS, {} === {} evaluates to false . So in the eyes of useEffect , genresObj is different every time, causing it to recall your D3 logic.

Changing useEffect to depend on userTopArtistsAllTime , like has been suggested, makes sense. With that change, you will recall the effect as often as userTopArtistsAllTime changes === equality. If you make that change and you still see the useEffect firing too often, check to see what logic is constructing userTopArtistsAllTime .

Now, it seems that you currently have a bug with your getData implementation. Make it a pure function of its arguments to correctly handle side effects.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM