Skip to content Skip to sidebar Skip to footer

How To Run Effect Depending On If A Value Increases Or Decreases In React?

This is an expansion on a previous question asking how to run an effect by adding an HTML class once some data changes. In this case, I'd like to compare the new and old values. If

Solution 1:

Let's continue off the solution from the previous answer (added below for easier reference).

functionuseHasRecentlyChanged(variable, timeout = 2000) {
  const firstRender = useRef(true);
  const [hasRecentlyChanged, setHasRecentlyChanged] = useState(false);

  useEffect(() => {
    if (firstRender.current) {
      return;
    }

    setHasRecentlyChanged(true);

    setTimeout(() => {
      setHasRecentlyChanged(false);
    }, timeout);
  }, [variable]);

  useEffect(() => {
    if (firstRender.current) {
      firstRender.current = false;
    }
  });

  return hasRecentlyChanged;
}

It currently returns true if the variable passed to it has changed and then returns false after the timeout time has elapsed. We want to change this function so that it communicates if the value passed to it has increased or decreased recently. The arguments can remain the same but we need to change the interface of the return. We can't use booleans any more because we now have three possibilities: increased, no change and decreased. I'm going to go with positive integer, zero and negative integer to represent each respectively because we can subtract the current value with the previous value to determine if it has increased or decreased. If the subtraction value is greater than 0 we know the current value has increased, if the subtraction value is less than 0 we know the current value has decreased and if the subtraction value is 0 we know there's no change.

increased = 1
no change = 0
decreased = -1

We'll also need to add something to compare the current value with the previous value. We can use the useRef hook to store the value for the next render. There's even an example of this in the current solution: we store the firstRender state in a ref. Let's make these changes to the above function:

functionuseHasRecentlyIncreasedOrDecreased(variable, timeout = 2000) {
  const firstRender = useRef(true);
  const previousValue = useRef();
  const [state, setState] = useState(0);

  useEffect(() => {
    if (firstRender.current) {
      previousValue.current = variable;
      return;
    }

    setState(Math.min(1, Math.max(-1, variable - previousValue.current)))
    
    previousValue.current = variable;

    setTimeout(() => {
      setState(0);
    }, timeout);
  }, [variable]);

  useEffect(() => {
    if (firstRender.current) {
      firstRender.current = false;
    }
  });

  return state;
}

After this, add the effects you want depending on the value returned from this hook. Below is a demo:

const { useEffect, useRef, useState } = React;
const rootElement = document.getElementById('root');

functionuseHasRecentlyIncreasedOrDecreased(variable, timeout = 2000) {
  const firstRender = useRef(true);
  const previousValue = useRef();
  const [state, setState] = useState(0);

  useEffect(() => {
    if (firstRender.current) {
      previousValue.current = variable;
      return;
    }

    setState(Math.min(1, Math.max(-1, variable - previousValue.current)))
    
    previousValue.current = variable;

    setTimeout(() => {
      setState(0);
    }, timeout);
  }, [variable]);

  useEffect(() => {
    if (firstRender.current) {
      firstRender.current = false;
    }
  });

  return state;
}

functiongetRandomInt(min, max) {
  const ceilMin = Math.ceil(min);
  const floorMax = Math.floor(max);

  returnMath.floor(Math.random() * (floorMax - ceilMin + 1)) + ceilMin;
}

functionListItem({ key, children }) {
  const increasedOrDecreased = useHasRecentlyIncreasedOrDecreased(children);
  
  return (
    <likey={key}className={increasedOrDecreased > 0 ? 'increased' : increasedOrDecreased < 0 ? 'decreased' : undefined}>{children}</li>
  );
}

functionApp() {
  const [items, setItems] = useState([
    getRandomInt(0, 1000),
    getRandomInt(0, 1000),
    getRandomInt(0, 1000),
    getRandomInt(0, 1000),
    getRandomInt(0, 1000),
    getRandomInt(0, 1000),
    getRandomInt(0, 1000),
    getRandomInt(0, 1000),
    getRandomInt(0, 1000),
  ]);
  
  useEffect(() => {
    const intervalId = setInterval(() => { 
      const newItems = [...items];
      const updateIndex = getRandomInt(0, items.length - 1);
      newItems[updateIndex] = getRandomInt(0, 1000);
      setItems(newItems);
    }, 2000);
    
    return() =>clearInterval(intervalId);
  }, [...items]);
 
  return (
    <ul>
      {items.map((item, index) => <ListItemkey={index}>{item}</ListItem>)}
    </ul>
  );
}

ReactDOM.render(
  <App />,
  rootElement
);
body {
  font-family: monospace;
  font-size: 20px;
}

li {
  margin: 5px0;
}

.increased {
  color: green;
}

.increased::after {
  content: " ⬆️"
}

.decreased {
  color: red;
}

.decreased::after {
  content: " ⬇️"
}
<divid="root"></div><scriptcrossoriginsrc="https://unpkg.com/react@17/umd/react.production.min.js"></script><scriptcrossoriginsrc="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

Post a Comment for "How To Run Effect Depending On If A Value Increases Or Decreases In React?"