Preventing event from executing consecutive times React

Viewed 292

I have this parent component that has this state which is an object, one of the properties is a function called onScrollbarSelection, that makes an axios post. All of this is going to be passed to the child component.

The child component uses an API from Highcharts, one of the properties ( afterSetExtremes ) of the API uses the onScrollbarSelection function from the parent (props). The problem is that afterSetExtremes can trigger a lot of consecutive times which is making the onScrollbarSelection function execute a lot of consecutive times as well and making a lot of axios posts, crashing the browser.

I've tried putting a timeout on the post call, but didn't work, it still calls makes the axios post a lot of times. How can I prevent from onScrollbarSelection (or the afterSetExtremes event) from executing a lot of consecutive times?

const Parent = () => {
    const [state,setState] = useState({
        onScrollbarSelection: (e) => {
            var min = new Date(e.min);
            var max = new Date(e.max);
            var minDate = min.getFullYear() + '-' + (min.getMonth() + 1) + '-' + min.getDate();
            var maxDate = max.getFullYear() + '-' + (max.getMonth() + 1) + '-' + max.getDate();
            async function postData() {
                axios.post('url',{
                    start_date: minDate,
                    end_date: maxDate,
                });
            }
            setTimeout(postData(),3000);
        }
    });

    useEffect( () => {
        async function fetchData() {
            var res = await axios.get(url);
            setState({
                ...state,
                data: res.data,
            });
        }
        fetchData();
    },[]);



   return(
        <div>
             <Child config={state}/>
        </div>
   )

};

const Child = (props) => {
    const state = {
        xAxis:{
            events: {
                afterSetExtremes: (e) => { //this triggers a lot of consecutive times
                    props.config.onScrollbarSelection(e);
                }
            }

        }, 
    };

    return (
        <div>
            <HighchartsReact highcharts={Highcharts} constructorType={'stockChart'} options={state} />
        </div>
    );
}
2 Answers

You should use the function with debounce effect.

const Parent = () => {
  const [data, setDate] = React.useState(); 
  const [state, setState] = React.useState([]);
  
  const afterSetExtremes = React.useCallback(({ min, max }) => {
    setState([min, max]);
  }, []);
  /* create option for HighchartsReact */
  const option = React.useMemo(() => ({
    xAxis: {
      events: {
        afterSetExtremes,
      },
    },
  }), [afterSetExtremes]);
  /* get data from url end set it to data state */
  useEffect( () => {
    axios.get('url')
      .then((res) => {
        setDate(res.data);
      })
  },[]);
  /* do post if state changed, works with debounce 250ms */
  useEffect(() => {
    const handler = setTimeout(() => {
      const [minDate, maxDate] = state.map((value) => {
        const d = new Date(value);
        return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
      });
      
      axios.post('url', { start_date: minDate, end_date: maxDate })
        .then((res) => {})
        .catch((error) => {});
    }, 250);
    
    return () => {
      /* if state changed in 250ms
       * clearTimeout remove handler
       * and axios.post does not call.
       */
      clearTimeout(handler);
    };
  }, [state]);

  return (
    <HighchartsReact
      highcharts={Highcharts}
      constructorType='stockChart'
      options={option}
    />
  );
};

Related