简体   繁体   中英

React component not getting rendered each time when state changed

I am trying to sort an array using bubble sort and at each step of sort I want to render the change in position of values inside an array. So made the below algorithm with React, at very first time it does render the changes but then gets stop, doesn't render anything. I tried to console log the array I am rendering in and I am getting the each sort step on console but don't know why it is not working here. Can anybody please tell me why it's happening so? also what can be done?

Note: It was working fine and rendering the sorted array initially, but later when I added setTimeOut function it is not working.

App.js

import React, { useState } from "react";

const App = () => {
  const [arrayForSort, setArrayForSort] = useState([44,2,46,11,15,34,1,7,55]);

  const fetchArray = arrayForSort.map((element) => {
    return <span>{element} </span>;
  });

  const bubbleSort = (array) => {
    let newArray = [...array];
    let n = newArray.length;

    for (let i = 0; i < n; i++) {
      for (let j = 0; j < n - i; j++) {
        const myVar = setTimeout(() => {
          if (newArray[j] > newArray[j + 1]) {
            [newArray[j], newArray[j + 1]] = [newArray[j + 1], newArray[j]];
          }
          console.log(newArray);   // render each step of sort 
          setArrayForSort(newArray);   // on screen it just shows "2 44 46 11 15 34 1 7 55" the very first step of sort
        }, 2000);
      }
    }
  };

  return (
    <div>
      <h4>Array: </h4>
      <span>{fetchArray}</span>
      <div>
        <button
          onClick={() => {
            bubbleSort(arrayForSort); 
          }}
        >
          Sort
        </button>
      </div>
    </div>
  );
};

export default App;

There are few problems in your code. 2000 is a constant value, meaning all your setTimeout s are fired together after 2 seconds. That's why all your console.logs appear at the same time.

Secondly, setTimeout will retain the reference of setArrayForSort . But you need a new reference after each render. This could work if you use class components but with functional component you need to think differently.

Here is a working demo codesandbox link

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

const App = () => {
  const [arrayForSort, setArrayForSort] = useState([44, 2, 46, 11, 15, 34, 1, 7, 55]);

  const ijRef = useRef({ i: 0, j: 0 });
  const [isSorting, setIsSorting] = useState(false);

  const fetchArray = arrayForSort.map((element) => {
    return <span key={element}>{element} </span>;
  });

  useEffect(() => {
    if (!isSorting) return;

    const ij = ijRef.current;
    const i = ij.i;
    const j = ij.j;
    const arr = [...arrayForSort];
    const n = arr.length;
    let isSwapped = false;

    if (arr[j] > arr[j + 1]) {
      [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      isSwapped = true;
    }

    if (j < n - i) {
      ij.j++;
    } else if (ij.i < n) {
      ij.j = 0;
      ij.i++;
    }

    // This will trigger the next render
    // and loop will continue until ij.i < n
    setTimeout(
      () => {
        if (ij.i >= n) {
          setIsSorting(false);
        }
        setArrayForSort(arr);
      },
      isSwapped ? 100 : 0 // delay 100ms for a successful swap
    );
  }, [ijRef, arrayForSort, setArrayForSort, isSorting]);

  const beginSort = () => {
    ijRef.current.i = 0;
    ijRef.current.j = 0;
    setIsSorting(true); // begin sorting
  };

  return (
    <div>
      <h4>Array: </h4>
      <span>{fetchArray}</span>
      <div>
        <button onClick={beginSort}>Sort</button>
      </div>
    </div>
  );
};

export default App;

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