Yoctol / react-d3-cloud

A word cloud react component built with d3-cloud.
https://yoctol.github.io/react-d3-cloud
MIT License
141 stars 47 forks source link

Cloud re renders content on any on click event on the same page. #97

Closed Ankcorn closed 3 years ago

Ankcorn commented 6 years ago

For every onclick event on the page the wordcloud is displayed on all the words shuffle about a bit.

The data prop is provided from redux but does not change prompting a re render of the chart

const fontSizeMapper = (word, data) =>
  (Math.log2(((word.value / data[0].value) * 5) + 1.6) * 14) - 1;

const Cloud = ({ title, data, onWordClick }) => (
  <Card>
    <CardContent>
      {title}
    </CardContent>
    <WordCloud
      data={data}
      onWordClick={onWordClick}
      fontSizeMapper={(word) => {
        return fontSizeMapper(word, data);
      }}
      font="roboto"
    />
  </Card>
);

If I have implemented it wrong let me know.

Thanks

julianaubrey commented 5 years ago

No fix on this yet?

julianaubrey commented 5 years ago

I don't know if this could help but I used the componentShouldUpdate() lifecycle hook to stop the re-rendering of the word cloud. In my case, I was passing the data used by the word cloud as props. So I return false in the componentShouldUpdate() lifecycle hook if the data is equal. Word of caution though, you must ensure that you are comparing only the text and value keys because other keys (e.g., padding, font, x, y, etc.) seem to be automatically added by the WordCloud component. Let me know if this helps. Thanks!

nileshlaxmi commented 5 years ago

We resolved this issue using PureComponents. It disabled re-rendering content on any click event. We made our component(parent) PureComponent where we are calling WordCloud component

chentsulin commented 3 years ago

As of version 0.10.1, <WordCloud /> has been wrapped by React.memo() and deep equal comparison under the hood to avoid unnecessary re-render. All you need to do is to make your function props deep equal comparable using useCallback():

import React, { useCallback } from 'react';
import { render } from 'react-dom';
import WordCloud from 'react-d3-cloud';
import { scaleOrdinal } from 'd3-scale';
import { schemeCategory10 } from 'd3-scale-chromatic';

function App() {
  const data = [
    { text: 'Hey', value: 1000 },
    { text: 'lol', value: 200 },
    { text: 'first impression', value: 800 },
    { text: 'very cool', value: 1000000 },
    { text: 'duck', value: 10 },
  ];

  const fontSize = useCallback((word) => Math.log2(word.value) * 5, []);
  const rotate = useCallback((word) => word.value % 360, []);
  const fill = useCallback((d, i) => scaleOrdinal(schemeCategory10)(i), []);
  const onWordClick = useCallback((word) => {
    console.log(`onWordClick: ${word}`);
  }, []);
  const onWordMouseOver = useCallback((word) => {
    console.log(`onWordMouseOver: ${word}`);
  }, []);
  const onWordMouseOut = useCallback((word) => {
    console.log(`onWordMouseOut: ${word}`);
  }, []);

  return (
    <WordCloud
      data={data}
      width={500}
      height={500}
      font="Times"
      fontStyle="italic"
      fontWeight="bold"
      fontSize={fontSize}
      spiral="rectangular"
      rotate={rotate}
      padding={5}
      random={Math.random}
      fill={fill}
      onWordClick={onWordClick}
      onWordMouseOver={onWordMouseOver}
      onWordMouseOut={onWordMouseOut}
    />
  );
);

See https://github.com/Yoctol/react-d3-cloud#how-to-avoid-unnecessary-re-render