Let's build a Sorting Visualizer using React - Part I

thumbnail

Hello friends, I am back with a new project where we will build a sorting visualizer using React JS and I will use Chakra UI to speed up the UI development. Sorting Visualizer is an amazing project to strengthen your React skills. We can also learn about the algorithm. So let’s get started.

Create a new React project using

npx create-react-app sorting-visualizer-react

Once created cleanup the basic code at App.js and remove App.css

As told earlier I will use chakra ui to speed up UI development, so we will install the chakra ui using

npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^6

To know more about the chakra ui and installation guides you can refer the below documentation.

Chakra UI | Documentation

Let’s start with the code.

In index.js we will wrap our App component inside ChakraProvider to make use chakra UI component.

...
import { ChakraProvider } from "@chakra-ui/react";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <ChakraProvider>
      <App />
    </ChakraProvider>
  </React.StrictMode>
);
....

output.webp

Above is the UI which we will develop as a base version for this app. We have two components Sidebar and the Visualizer component. So create a components folder under src, and create two components Sidebar & Visualizer.

Inside Sidebar, we have a form elements slider to select array size, a dropdown for selecting algorithm, and two buttons one for generating a new array and the other for sorting the array. We have utilized the chakra UI Slider component for selecting the array size, and the Select component for dropdown. We have generateArr and sort as props which is a function and will be called when generate array and sort button is clicked respectively.

import {
  Box,
Button,Flex,FormControl,FormLabel,Select,Slider,SliderFilledTrack,SliderThumb,
  SliderTrack } from "@chakra-ui/react";
import React, { useState } from "react";

const Sidebar = ({ generateArr, sort }) => {
  const [formState, setFormState] = useState({
    size: 10,
    algorithm: "linear",
  });
 
  return (
    <Box minW="xs" bg="gray.100" p={"4"} borderRadius="lg" overflow="hidden">

      <FormControl>
        <FormLabel htmlFor="size">{`Array Size (${formState.size})`}</FormLabel>
        <Slider
          onChangeEnd={(v) => setFormState({ ...formState, size: v })}
          aria-label={"10"}
          min={10}
          max={200}
          defaultValue={10}
        >
          <SliderTrack>
            <SliderFilledTrack />
          </SliderTrack>
          <SliderThumb />
        </Slider>
      </FormControl>
      <FormControl>
        <FormLabel htmlFor="algorithm">Algorithm</FormLabel>
        <Select
          onChange={(v) => setFormState({ ...formState, algorithm: v })}
          bg={"white"}
          id="algorithm"
          variant={"outline"}
        >
          <option value="simple">Simple</option>
          <option value="select">Selection</option>
          <option value="insert">Insertion</option>
        </Select>
      </FormControl>
      <Flex gap={"3"} mt={"5"} direction="row">
        <Button

          onClick={() => {
            generateArr(formState);
          }}

          colorScheme="purple"
        >
          Generate Array
        </Button>

        <Button

          onClick={sort}
          colorScheme="purple"
          variant="outline"
        >
          Sort
        </Button>
      </Flex>
    </Box>
  );
};

export default Sidebar;

For the Visualizer component, we need to pass data as props which is an array, and we will map through it and display the visual bars wrapped under the auto column grid.

import { Box } from "@chakra-ui/react";
import React from "react";

const Visualiser = ({ data }) => {
  return (
    <Box
      rounded={"lg"}
      display="grid"
      gridAutoFlow={"column"}
      gridAutoColumns={"auto"}
      bg="gray.100"
      minH={"full"}
      overflow={"auto"}
      flex="1"
    >
      {data.map((d) => {
        return (
          <Box
            display={"flex"}
            justifyContent="flex-end"
            textAlign="center"
            flexDirection="column"
          >
            <p>{d}</p>
            <Box
              roundedTop={"sm"}
              border={"1px"}
              borderColor={"purple.200"}
              bg={"purple.300"}
              style={{ height: `${d}px` }}
            ></Box>
          </Box>
        );
      })}
    </Box>
  );
};

export default Visualiser;

Now in App.js, we will return these two components under row flex. Now we will create state variables that need to be passed to the Sidebar & Visualizer components. Create an arr variable to store numbers that need to be sorted, generating (boolean variable to check whether a new array is generating or not), sorting (boolean variable to check whether an array is soring or not).

...
function App() {
  const [arr, setArr] = useState([]);
  const [generating, setGenerating] = useState(false);
  const [sorting, setSorting] = useState(false);
...
  return (
    <Box p={"4"}>
      <Flex gap={"4"}>
        <Sidebar
          generateArr={generateArr}

          sort={sort}
        />
        <Visualiser data={arr} />
      </Flex>
    </Box>
}

Now for the functions, we need to create a function for generating a random array initialize an empty array and we will loop until the length becomes the size we got through form and we will add a random number, and before adding we will check whether the number is present or not. Pass this function as a prop to the sidebar component and we will call this function when generating array button is clicked and pass the formate which contains array size and selected algorithm. We will also call the generate array when a component is mounted, so create a useEffect with an empty dependency array and call generateArray().

...
const generateArr = (formState) => {
    
      let newArr = [];
      while (newArr.length <= formState.size) {
        let random = Math.floor(Math.random() * (200 - 10) + 10);
        if (newArr.indexOf(random) === -1) {
          newArr.push(random);
        }
      }
      setArr([...newArr]);

  };
useState(() => {
    generateArr({ size: 10 });
  }, []);
...

For the sort function as of now, we have a simple sorting algorithm where we use a nested for loop to iterate over the array and check if the current element is greater than the next element if greater then swap it with that element. Again pass this to the sort function as a prop to the sidebar component and call this when the sort button is clicked. At this point, you could see everything would work fine, but all happens in a single process, if we do this we cannot do visualize or implement any animation or other features which we will include in upcoming posts. So we will use setTimeout to delay the outer and inner for loop such that the outer is delayed more than the inner. I have set this delay time after experimenting with different timer so I recommend you to try adjusting and experimenting with that.

const sort = () => {
    setTimeout(() => {
      let newArr = [...arr];
      for (let i = 0; i < arr.length - 1; i++) {
        setTimeout(() => {
          for (let j = i + 1; j < arr.length; j++) {
            if (newArr[i] > newArr[j]) {
              let temp = newArr[i];
              newArr[i] = newArr[j];
              newArr[j] = temp;
              let newStep = [...newArr];

              setTimeout(() => {
                setArr([...newStep]);
              }, j * 100);
            }
            
          }
        }, i * 1000);
      }
    }, 500);
  };

Now for some small improvements, we will set generating to true when generating array function is called and again set it to false when the function completes. Here, when I was setting the change so fast that it sets to true and sets to false immediately, so we will again use setTimeout to delay after the generating set to true. Similarly, we will set sorting to true or false. Based on this we show the loading or disable button with the help of the isLoading & disabled prop for the chakra-UI Button component. You can adjust the delay by testing with different timers.

...
const generateArr = (formState) => {
    setGenerating(true);
    setTimeout(() => {
      ...
      setGenerating(false);
    }, 500);
  };
...
const sort = () => {
    setSorting(true);
    setTimeout(() => {
      ...
    }, 500)
}

...

For Live Demo you can check on the below link.

Demo Link

You can try adding other algorithm. This is just the initial version of the app, we will work on this to implement more features on it in our upcoming posts.

You can find this code on my Github repository.

Sorting Visualizer React | Github

Thanks for reading this post, if you found this post helpful please share maximum, Thanks for reading 😊 Stay tuned.

If you are facing any issues please contact us from our contact section.

Contact Us | CodeWithMarish

Also please don’t forget to subscribe to our youtube channel codewithmarish for all web development-related challenges.

Code With Marish | Youtube

Related Posts