Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
214 views
in Technique[技术] by (71.8m points)

javascript - React stop rerendering of parent component when slider is used

UPDATE based on Jayce444's comment. I moved setting the timer outside of the component and this helped. Is a global variable the best idea tho?

let globalTimer = null;
const setSlidingTimer = (timer) => {
  globalTimer = timer;
};

const SearchJobs = (props) => {
  useEffect(() => console.log("<SearchJobs /> rendered"));

const [sliderValue, setSliderValue] = useState(20);

const sliderChangeHandler = (newValue) => {
    clearTimeout(globalTimer);
    const timer = setTimeout(setSliderValue, 300, newValue);
    setSlidingTimer(timer);
  };

ORIGINAL POST:

My React app suffers in performance from too many rerenders of a parent component because its children (mainly a slider) change the state too often when changing their value.

My idea (see sliderChangeHandler) was to wait sth like 300ms before updating the slider's value in the state to reduce the number of rerenders. Somehow this doesn't work yet. Is there anyone who could help me with especially the slider and apply a similar idea to the input box? Like wait 300ms before updating the state. If the user types or slides again, reset the 300ms timer.

Here is the parent code

import React, { useEffect, useState } from "react";

import Col from "react-bootstrap/Col";
import Button from "react-bootstrap/Button";
import InputGroup from "react-bootstrap/InputGroup";
import FormControl from "react-bootstrap/FormControl";

import Slider from "../UI/Slider/Slider";

const SearchJobs = (props) => {
  useEffect(() => console.log("<SearchJobs /> rendered"));

  const [sliderValue, setSliderValue] = useState(20);
  const [address, setAddress] = useState("");
  const [slidingTimeout, setSlidingTimeout] = useState("");

  const sliderChangeHandler = (event, newValue) => {
    clearTimeout(slidingTimeout);
    const timer = setTimeout(setSliderValue, 300, newValue);
    setSlidingTimeout(timer);
  };

  return (
    <>
      <Col xs={12} md={7}>
        <InputGroup className="mb-3">
          <InputGroup.Prepend>
            <InputGroup.Text id="inputGroup-sizing-default">
              Adresse
            </InputGroup.Text>
          </InputGroup.Prepend>
          <FormControl
            aria-label="Address"
            aria-describedby="inputGroup-sizing-default"
            placeholder="myAddress"
            value={address}
            onChange={(event) => setAddress(event.target.value)}
          />
        </InputGroup>
      </Col>
      <Col xs={12} md={5}>
        <Slider value={sliderValue} change={sliderChangeHandler} />
      </Col>
    </>
  );
};

export default SearchJobs;

And this is the slider component:

import React from "react";
import Typography from "@material-ui/core/Typography";
import Slider from "@material-ui/core/Slider";

const SliderComponent = (props) => {
  const marks = [
    {
      value: 0,
      label: "0 km",
    },

    {
      value: 50,
      label: "50 km",
    },
  ];

  function valuetext(value) {
    return `${value} km`;
  }

  return (
    <div className="ml-3 mr-3">
      <Typography id="discrete-slider-always" gutterBottom>
        Radius
      </Typography>
      <Slider
        defaultValue={20}
        value={props.sliderValue}
        onChange={(event, newValue) => props.change(event, newValue)}
        getAriaValueText={valuetext}
        aria-labelledby="discrete-slider-always"
        step={0.1}
        min={0}
        max={50}
        marks={marks}
        valueLabelDisplay="on"
      />
    </div>
  );
};

export default SliderComponent;

question from:https://stackoverflow.com/questions/65910561/react-stop-rerendering-of-parent-component-when-slider-is-used

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You can use a generic debouncing function, for example:

const debounce = (inputFun, milliseconds) => {
  let timeout;

  return (...args) => {
    if(!timeout) {
      inputFun(...args);

      timeout = setTimeout(
        () => ( timeout = undefined ), 
        milliseconds
      );
    }
  };
};

Then u create a debounced sliderChangeHandler like this:

const sliderChangeHandler = debounce(event => {
    setSliderValue(event.target.value);
}, 300);

U might want to adjust the arguments of the sliderChangeHandler (don't know why u have event and newValue) to match your requirements.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...