首页 > 解决方案 > 如何使一个组件中的按钮 onClick 调用另一个组件中的函数 React

问题描述

我有一个反应应用程序,它有一个导航栏和一个网格系统。这个 NavBar 显示一些信息并具有运行应用程序的按钮,网格系统包含功能并可视化应用程序。我想让 NavBar 按钮单击触发一个函数来调用网格系统组件,特别是 animateAlgorithm 函数。如您所见,我已经有一个提供程序在这两个组件之间共享一些状态信息。我不明白的是如何让它调用函数。

https://github.com/austinedger0811/path-finding-visualization

应用程序.js

import GridContainer from './Components/GridContainer'
import NavBar from './Components/NavBar'

import {OptionsProvider} from './Context/OptionsContext'

import './App.css';

function App() {

  return (
    <OptionsProvider>
      <div className="App">
        <NavBar />
        <GridContainer rows={40} colums={40} />
      </div>
    </OptionsProvider>
  );
}

export default App;

导航栏.js

import React, { useState, useContext } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import AppBar from '@material-ui/core/AppBar'
import Toolbar from '@material-ui/core/Toolbar'
import Typography from '@material-ui/core/Typography'
import IconButton from '@material-ui/core/IconButton'
import MenuIcon from '@material-ui/icons/Menu'
import Button from '@material-ui/core/Button'
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import MenuItem from '@material-ui/core/MenuItem';
import Menu from '@material-ui/core/Menu';
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'

import { OptionsContext } from '../Context/OptionsContext'


const useStyles = makeStyles((theme) => ({
    root: {
      flexGrow: 1,
    },
    menuButton: {
      marginRight: theme.spacing(2),
    },
        button: {
            marginRight: theme.spacing(2),
        },
    title: {
        marginRight: theme.spacing(12)
    },
        formControl: {
            margin: theme.spacing(1),
            minWidth: 120,
        },
        listItemSecondaryText: {
            color: 'lightgray',
        },
  }));

    const algorithmOptions = [
        'Breadth First Search',
        'Depth First Search',
        'Dijkstra\'s',
        'Other',
    ];

    const wallOptions = [
        'None',
        'Random Wall Generation',
        'Randomized Depth First Search',
        'Recursive Division'
    ];
    
function NavBar() {

    const classes = useStyles();

        const [anchorElAlgorithm, setAnchorElAlgorithm] = React.useState(null);
        const [anchorElWall, setAnchorElWall] = React.useState(null);

        const { algorithmIndex, setAlgorithmIndex, wallIndex, setWallIndex } = useContext(OptionsContext);

        const handleClickAlgorithmListItem = (event) => {
            setAnchorElAlgorithm(event.currentTarget);
        };

        const handleClickWallListItem = (event) => {
            setAnchorElWall(event.currentTarget);
        };

        const handleAlgorithmItemClick = (event, index) => {
            setAlgorithmIndex(index);
            setAnchorElAlgorithm(null);
        };

        const handleWallItemClick = (event, index) => {
            setWallIndex(index);
            setAnchorElWall(null);
        }
    
        const handleClose = () => {
            setAnchorElAlgorithm(null);
            setAnchorElWall(null);
        };

    return (
            <div className={classes.root}>
                <AppBar position="static">
                    <Toolbar>
                        <IconButton className={classes.menuButton} edge="start" color="inherit" aria-label="menu">
                            <MenuIcon />
                        </IconButton>
                        <Typography className={classes.title} variant="h6">Path Finding Visualizer</Typography>
                        <List component="nav" aria-label="algorithm selector">
                            <ListItem
                                button
                                aria-aria-haspopup="true"
                                aria-controls="search-algorithm"
                                aria-label="Search Algorithm"
                                onClick={handleClickAlgorithmListItem}
                            >
                                <ListItemText primary="Search Algorithm" secondary={algorithmOptions[algorithmIndex]} classes={{ secondary: classes.listItemSecondaryText }} />
                            </ListItem>
                        </List>
                        <Menu
                            id="search-algorithm"
                            anchorEl={anchorElAlgorithm}
                            keepMounted
                            open={Boolean(anchorElAlgorithm)}
                            onClose={handleClose}
                        >
                            {algorithmOptions.map((option, index) => (
                                <MenuItem
                                    key={algorithmOptions}
                                    selected={index === algorithmIndex}
                                    onClick={(event) => handleAlgorithmItemClick(event, index)}
                                >
                                    {option}
                                </MenuItem>
                            ))}
                        </Menu>


                        <List component="nav" aria-label="add walls">
                            <ListItem
                                button
                                aria-aria-haspopup="true"
                                aria-controls="add-walls"
                                aria-label="Add Walls"
                                onClick={handleClickWallListItem}
                            >
                                <ListItemText primary="Wall Generation" secondary={wallOptions[wallIndex]} classes={{ secondary: classes.listItemSecondaryText }} />
                            </ListItem>
                        </List>
                        <Menu
                            id="add-walls"
                            anchorEl={anchorElWall}
                            keepMounted
                            open={Boolean(anchorElWall)}
                            onClose={handleClose}
                        >
                            {wallOptions.map((option, index) => (
                                <MenuItem
                                    key={wallOptions}
                                    selected={index === wallIndex}
                                    onClick={(event) => handleWallItemClick(event, index)}
                                >
                                    {option}
                                </MenuItem>
                            ))}
                        </Menu>
                        <Button variant="contained" color="secondary" className={classes.button} disableElevation>Run Visualization</Button>
                        <Button variant="contained" color="secondary" className={classes.button} disableElevation>Reset</Button>
                    </Toolbar>
                </AppBar> 
      </div>
    )
}

export default NavBar

GridContainer.js

import React, {useState, useEffect, useContext } from 'react'
import makeStyles from '@material-ui/core/styles/makeStyles'
import Button from '@material-ui/core/Button'
import Node from './Node'

import { OptionsContext } from '../Context/OptionsContext'

import './Grid.css'

const useStyles = makeStyles({
    root: {
        display: 'flex',
        justifyContent: 'center'
    },
    grid: props => ({
        display: 'grid',
        gridTemplateColumns: `repeat(${props.colums}, 1fr)`, 
        gridTemplateRows: `repeat(${props.rows}, 1fr)`,
        alignSelf: 'flex-start',
        width: props.colums * 20,
        height: props.rows * 20,
        justifyContent: 'center',
    }),
});

function GridContainer(props) {

    let classes = useStyles(props);
    const { rows, colums } = props;
    const { algorithmIndex, wallIndex } = useContext(OptionsContext);

    var visited = [];
    var path = [];
    const [Grid, setGrid] = useState([]);

    useEffect(() => {
        initGrid();
    }, []);

    var start = [4, rows / 2];
    var end = [colums - 5, colums / 2];

    const initGrid = () => {
        var grid = [];
        for (let row = 0; row < rows; row++) {
            grid.push([])
            for (let col = 0; col < colums; col++) {
                grid[row].push(createNode(row, col));
            }
        }
        setGrid([...grid]);
    }

    const createNode = (row, col) => {
        return {
            row,
            col,
            isStart: row === start[0] && col === start[1],
            isEnd: row === end[0] && col === end[1],
            isVisited: false,
            isWall: false,
            isPath: false,
            distance: Infinity,
            prevNode: null,
        };
     };

     const bfs = () => {

        var location = {
            row: start[0],
            col: start[1],
        };
    
        var grid = Grid;
        var queue = [];
        queue.push(location);
    
        while (queue.length) {
            var currentLocation = queue.shift();
            var row = currentLocation.row;
            var col = currentLocation.col;
            if (row === end[0] && col === end[1]) {
                setGrid(...[grid]);
                getPath(grid);
                return true;
            }
            if (grid[row][col].isVisited === false) {
                grid[row][col].isVisited = true;
                visited.push(grid[row][col]);
            }else {
                continue;
            }
            var neighbors = getNeighbors(grid, row, col);
            for (let neighbor of neighbors) {
                if (grid[neighbor.row][neighbor.col].isVisited !== true) {
                    queue.push(neighbor);
                    grid[neighbor.row][neighbor.col].prevNode = currentLocation;
                }
            }
        }

        return false;
    };

    const getNeighbors = (grid, row, col) => {
        let neighbors = [];
        if (validNode(grid, row, col - 1)) {
            neighbors.push({
                row: row,
                col: col - 1,
            });
        }
        if (validNode(grid, row, col + 1)) {
            neighbors.push({
                row: row,
                col: col + 1,
            });
        }
        if (validNode(grid, row - 1, col)) {
            neighbors.push({
                row: row - 1,
                col: col,
            });
        }
        if (validNode(grid, row + 1, col)) {
            neighbors.push({
                row: row + 1,
                col: col,
            });
        }
        return neighbors;
    };

    const validNode = (grid, row, col) => {
        var rowLength = grid.length;
        var colLength = grid[0].length;
        if (row < 0 || row >= rowLength || col < 0 || col >= colLength) {
            return false;
        }
        if (grid[row][col].isWall) {
            return false;
        }
        return true;
    };

    const getPath = (grid) => {
        var currentNodeCord = {
            row: end[0],
            col: end[1],
        }
        path.push(currentNodeCord);
        var curRow = currentNodeCord.row;
        var curCol = currentNodeCord.col;
        var prevNodeCord = grid[curRow][curCol].prevNode;
        while (prevNodeCord !== null) {
            currentNodeCord = prevNodeCord;
            path.push(currentNodeCord);
            curRow = currentNodeCord.row;
            curCol = currentNodeCord.col;
            var curNode = grid[curRow][curCol];
            prevNodeCord = curNode.prevNode;
        }
        path.reverse();
    };

    const animateAlgorithm = () => {
        
        bfs();
        console.log(`Visited length: ${visited.length}`)
        for (let i = 1; i <= visited.length; i++) {
            if (i === visited.length) {
                setTimeout(() => {
                    drawPath();
                }, 7 * i);
            } else {
                let nodeCord = visited[i];
                let row = nodeCord.row;
                let col = nodeCord.col;
                setTimeout(() => {
                    markVisited(row, col);
                }, 6 * i);  
            }
        }
    };

    const drawPath = () => {
        for (let i = 1; i < path.length - 1; i++) {
            let nodeCord = path[i];
            let row = nodeCord.row;
            let col = nodeCord.col;
            setTimeout(() => {
                markPath(row, col);
            }, 40 * i);
        }
    };

    const markPath = (row, col) => {
        document.getElementById(`node-${row}-${col}`).className = 'node path';
    };

    const markVisited = (row, col) => {
        document.getElementById(`node-${row}-${col}`).className = 'node visited';
    };

    const addRandomWalls = (threshold) => {
        let grid = [...Grid];
        for (let row = 0; row < rows; row++) {
            for (let col = 0; col < colums; col++) {
                if (!grid[row][col].isStart && !grid[row][col].isEnd) {
                    grid[row][col].isWall = (Math.floor(Math.random() * 10) > threshold);
                }
            }
        }
        setGrid(grid);
    };

    const resetGridColors = () => {
        for (let row = 0; row < rows; row++) {
            for (let col = 0; col < colums; col++) {
                if (Grid[row][col].isStart !== true && Grid[row][col].isEnd !== true){
                    document.getElementById(`node-${row}-${col}`).className = 'node';
                }
            }
        }
    };

    const logStates = () => {
        console.log(Grid)
        console.log(visited)
        console.log(path)
    };

    const reset = () => {
        visited = [];
        path = [];
        resetGridColors();
        initGrid();
    };

    var GridMap = Grid.map((row, rowIndex) => {
        return (
            <div key={rowIndex}>
                {row.map((node, nodeIndex) => {
                    const { row, col, isStart, isEnd, isWall, isPath, isVisited } = node;
                    return (
                        <Node
                            key={`${row}${col}`}
                            width={20}
                            height={20}
                            row={row}
                            col={col}
                            isStart={isStart}
                            isEnd={isEnd}
                            isWall={isWall}
                            isPath={isPath}
                            isVisited={isVisited}
                        /> 
                    );
                })}
            </div>
        );
    })

    return (
        <>
            <div className={classes.root}>
                <div className={classes.grid}>
                    {GridMap}
                </div>
            </div>
            <Button variant="contained" color="primary" onClick={ () => animateAlgorithm() }>Animate Algorithm</Button>
            <Button variant="contained" color="primary" onClick={ () => addRandomWalls(6) }>Add Random Walls</Button>
            <Button variant="contained" color="primary" onClick={ () => reset() }>Reset</Button>
            <Button variant="contained" color="secondary" onClick={ () => logStates() }>Log Data</Button>   
        </>
    )
}

export default GridContainer

OptionsContext.js

import React, { useState, createContext, useMemo } from 'react'

export const OptionsContext = createContext();

export const OptionsProvider = (props) => {

    const [algorithmIndex, setAlgorithmIndex] = useState(0);
    const [wallIndex, setWallIndex] = useState(0);
    const menuValue= useMemo(() => ({
        algorithmIndex, setAlgorithmIndex,
        wallIndex, setWallIndex,
    }), [algorithmIndex, wallIndex]);

    return (
        <OptionsContext.Provider value={menuValue}>
            {props.children}
        </OptionsContext.Provider>
    );
};

任何帮助,将不胜感激!

标签: javascriptreactjs

解决方案


您需要做的是使用useImperativeHandle,因此您可以从组件外部访问该功能。

首先,调用useRef创建引用并将其传递给您的GridContaineras prop,稍后我们将ref在组件内部使用fowardRef. 然后,您需要将animateAlgorithm处理函数包装在内部并将其用作道具,NavBar以便在单击按钮时调用它。

应用程序.js

import React, {useRef} from 'react';
import GridContainer from './Components/GridContainer'
import NavBar from './Components/NavBar'

import {OptionsProvider} from './Context/OptionsContext'

import './App.css';

function App() {
  const containerRef = useRef(null);

  const handleClick = () => {
    if (containerRef?.current) containerRef.current.animateAlgorithm();
  };

  return (
    <OptionsProvider>
      <div className="App">
        <NavBar handleClick={handleClick} />
        <GridContainer ref={containerRef} rows={40} colums={40} />
      </div>
    </OptionsProvider>
  );
}

export default App;

现在我们需要正确使用handleClick定义在NavBar. 因此,使用道具设置onClick按钮的NavBar handleClick道具。您从未指定要单击哪个按钮,所以我假设带有Run Visualization内容的按钮。如果不是这个,您可以移动onClick到您希望的另一个按钮。

导航栏.js

import React, { useState, useContext } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import AppBar from '@material-ui/core/AppBar'
import Toolbar from '@material-ui/core/Toolbar'
import Typography from '@material-ui/core/Typography'
import IconButton from '@material-ui/core/IconButton'
import MenuIcon from '@material-ui/icons/Menu'
import Button from '@material-ui/core/Button'
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import MenuItem from '@material-ui/core/MenuItem';
import Menu from '@material-ui/core/Menu';
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'

import { OptionsContext } from '../Context/OptionsContext'


const useStyles = makeStyles((theme) => ({
    root: {
      flexGrow: 1,
    },
    menuButton: {
      marginRight: theme.spacing(2),
    },
        button: {
            marginRight: theme.spacing(2),
        },
    title: {
        marginRight: theme.spacing(12)
    },
        formControl: {
            margin: theme.spacing(1),
            minWidth: 120,
        },
        listItemSecondaryText: {
            color: 'lightgray',
        },
  }));

    const algorithmOptions = [
        'Breadth First Search',
        'Depth First Search',
        'Dijkstra\'s',
        'Other',
    ];

    const wallOptions = [
        'None',
        'Random Wall Generation',
        'Randomized Depth First Search',
        'Recursive Division'
    ];
    
function NavBar(props) {
    const {handleClick} = props;
    const classes = useStyles();

        const [anchorElAlgorithm, setAnchorElAlgorithm] = React.useState(null);
        const [anchorElWall, setAnchorElWall] = React.useState(null);

        const { algorithmIndex, setAlgorithmIndex, wallIndex, setWallIndex } = useContext(OptionsContext);

        const handleClickAlgorithmListItem = (event) => {
            setAnchorElAlgorithm(event.currentTarget);
        };

        const handleClickWallListItem = (event) => {
            setAnchorElWall(event.currentTarget);
        };

        const handleAlgorithmItemClick = (event, index) => {
            setAlgorithmIndex(index);
            setAnchorElAlgorithm(null);
        };

        const handleWallItemClick = (event, index) => {
            setWallIndex(index);
            setAnchorElWall(null);
        }
    
        const handleClose = () => {
            setAnchorElAlgorithm(null);
            setAnchorElWall(null);
        };

    return (
            <div className={classes.root}>
                <AppBar position="static">
                    <Toolbar>
                        <IconButton className={classes.menuButton} edge="start" color="inherit" aria-label="menu">
                            <MenuIcon />
                        </IconButton>
                        <Typography className={classes.title} variant="h6">Path Finding Visualizer</Typography>
                        <List component="nav" aria-label="algorithm selector">
                            <ListItem
                                button
                                aria-aria-haspopup="true"
                                aria-controls="search-algorithm"
                                aria-label="Search Algorithm"
                                onClick={handleClickAlgorithmListItem}
                            >
                                <ListItemText primary="Search Algorithm" secondary={algorithmOptions[algorithmIndex]} classes={{ secondary: classes.listItemSecondaryText }} />
                            </ListItem>
                        </List>
                        <Menu
                            id="search-algorithm"
                            anchorEl={anchorElAlgorithm}
                            keepMounted
                            open={Boolean(anchorElAlgorithm)}
                            onClose={handleClose}
                        >
                            {algorithmOptions.map((option, index) => (
                                <MenuItem
                                    key={algorithmOptions}
                                    selected={index === algorithmIndex}
                                    onClick={(event) => handleAlgorithmItemClick(event, index)}
                                >
                                    {option}
                                </MenuItem>
                            ))}
                        </Menu>


                        <List component="nav" aria-label="add walls">
                            <ListItem
                                button
                                aria-aria-haspopup="true"
                                aria-controls="add-walls"
                                aria-label="Add Walls"
                                onClick={handleClickWallListItem}
                            >
                                <ListItemText primary="Wall Generation" secondary={wallOptions[wallIndex]} classes={{ secondary: classes.listItemSecondaryText }} />
                            </ListItem>
                        </List>
                        <Menu
                            id="add-walls"
                            anchorEl={anchorElWall}
                            keepMounted
                            open={Boolean(anchorElWall)}
                            onClose={handleClose}
                        >
                            {wallOptions.map((option, index) => (
                                <MenuItem
                                    key={wallOptions}
                                    selected={index === wallIndex}
                                    onClick={(event) => handleWallItemClick(event, index)}
                                >
                                    {option}
                                </MenuItem>
                            ))}
                        </Menu>
                        <Button variant="contained" color="secondary" className={classes.button} onClick={handleClick} disableElevation>Run Visualization</Button>
                        <Button variant="contained" color="secondary" className={classes.button} disableElevation>Reset</Button>
                    </Toolbar>
                </AppBar> 
      </div>
    )
}

export default NavBar

最后,我们需要处理ref里面的GridContainer. 这样我们就可以通过 成功暴露 了animateAlgorithmref因此父组件可以直接调用它。

GridContainer.js

import React, {useState, useEffect, useContext, useImperativeHandle} from 'react'
import makeStyles from '@material-ui/core/styles/makeStyles'
import Button from '@material-ui/core/Button'
import Node from './Node'

import { OptionsContext } from '../Context/OptionsContext'

import './Grid.css'

const useStyles = makeStyles({
    root: {
        display: 'flex',
        justifyContent: 'center'
    },
    grid: props => ({
        display: 'grid',
        gridTemplateColumns: `repeat(${props.colums}, 1fr)`, 
        gridTemplateRows: `repeat(${props.rows}, 1fr)`,
        alignSelf: 'flex-start',
        width: props.colums * 20,
        height: props.rows * 20,
        justifyContent: 'center',
    }),
});

const GridContainer = React.forwardRef((props, ref) => {

    let classes = useStyles(props);
    const { rows, colums } = props;
    const { algorithmIndex, wallIndex } = useContext(OptionsContext);

    var visited = [];
    var path = [];
    const [Grid, setGrid] = useState([]);

    useEffect(() => {
        initGrid();
    }, []);

    var start = [4, rows / 2];
    var end = [colums - 5, colums / 2];

    const initGrid = () => {
        var grid = [];
        for (let row = 0; row < rows; row++) {
            grid.push([])
            for (let col = 0; col < colums; col++) {
                grid[row].push(createNode(row, col));
            }
        }
        setGrid([...grid]);
    }

    const createNode = (row, col) => {
        return {
            row,
            col,
            isStart: row === start[0] && col === start[1],
            isEnd: row === end[0] && col === end[1],
            isVisited: false,
            isWall: false,
            isPath: false,
            distance: Infinity,
            prevNode: null,
        };
     };

     const bfs = () => {

        var location = {
            row: start[0],
            col: start[1],
        };
    
        var grid = Grid;
        var queue = [];
        queue.push(location);
    
        while (queue.length) {
            var currentLocation = queue.shift();
            var row = currentLocation.row;
            var col = currentLocation.col;
            if (row === end[0] && col === end[1]) {
                setGrid(...[grid]);
                getPath(grid);
                return true;
            }
            if (grid[row][col].isVisited === false) {
                grid[row][col].isVisited = true;
                visited.push(grid[row][col]);
            }else {
                continue;
            }
            var neighbors = getNeighbors(grid, row, col);
            for (let neighbor of neighbors) {
                if (grid[neighbor.row][neighbor.col].isVisited !== true) {
                    queue.push(neighbor);
                    grid[neighbor.row][neighbor.col].prevNode = currentLocation;
                }
            }
        }

        return false;
    };

    const getNeighbors = (grid, row, col) => {
        let neighbors = [];
        if (validNode(grid, row, col - 1)) {
            neighbors.push({
                row: row,
                col: col - 1,
            });
        }
        if (validNode(grid, row, col + 1)) {
            neighbors.push({
                row: row,
                col: col + 1,
            });
        }
        if (validNode(grid, row - 1, col)) {
            neighbors.push({
                row: row - 1,
                col: col,
            });
        }
        if (validNode(grid, row + 1, col)) {
            neighbors.push({
                row: row + 1,
                col: col,
            });
        }
        return neighbors;
    };

    const validNode = (grid, row, col) => {
        var rowLength = grid.length;
        var colLength = grid[0].length;
        if (row < 0 || row >= rowLength || col < 0 || col >= colLength) {
            return false;
        }
        if (grid[row][col].isWall) {
            return false;
        }
        return true;
    };

    const getPath = (grid) => {
        var currentNodeCord = {
            row: end[0],
            col: end[1],
        }
        path.push(currentNodeCord);
        var curRow = currentNodeCord.row;
        var curCol = currentNodeCord.col;
        var prevNodeCord = grid[curRow][curCol].prevNode;
        while (prevNodeCord !== null) {
            currentNodeCord = prevNodeCord;
            path.push(currentNodeCord);
            curRow = currentNodeCord.row;
            curCol = currentNodeCord.col;
            var curNode = grid[curRow][curCol];
            prevNodeCord = curNode.prevNode;
        }
        path.reverse();
    };

    const animateAlgorithm = () => {
        
        bfs();
        console.log(`Visited length: ${visited.length}`)
        for (let i = 1; i <= visited.length; i++) {
            if (i === visited.length) {
                setTimeout(() => {
                    drawPath();
                }, 7 * i);
            } else {
                let nodeCord = visited[i];
                let row = nodeCord.row;
                let col = nodeCord.col;
                setTimeout(() => {
                    markVisited(row, col);
                }, 6 * i);  
            }
        }
    };

    useImperativeHandle(ref, () => ({
      animateAlgorithm
    });


    const drawPath = () => {
        for (let i = 1; i < path.length - 1; i++) {
            let nodeCord = path[i];
            let row = nodeCord.row;
            let col = nodeCord.col;
            setTimeout(() => {
                markPath(row, col);
            }, 40 * i);
        }
    };

    const markPath = (row, col) => {
        document.getElementById(`node-${row}-${col}`).className = 'node path';
    };

    const markVisited = (row, col) => {
        document.getElementById(`node-${row}-${col}`).className = 'node visited';
    };

    const addRandomWalls = (threshold) => {
        let grid = [...Grid];
        for (let row = 0; row < rows; row++) {
            for (let col = 0; col < colums; col++) {
                if (!grid[row][col].isStart && !grid[row][col].isEnd) {
                    grid[row][col].isWall = (Math.floor(Math.random() * 10) > threshold);
                }
            }
        }
        setGrid(grid);
    };

    const resetGridColors = () => {
        for (let row = 0; row < rows; row++) {
            for (let col = 0; col < colums; col++) {
                if (Grid[row][col].isStart !== true && Grid[row][col].isEnd !== true){
                    document.getElementById(`node-${row}-${col}`).className = 'node';
                }
            }
        }
    };

    const logStates = () => {
        console.log(Grid)
        console.log(visited)
        console.log(path)
    };

    const reset = () => {
        visited = [];
        path = [];
        resetGridColors();
        initGrid();
    };

    var GridMap = Grid.map((row, rowIndex) => {
        return (
            <div key={rowIndex}>
                {row.map((node, nodeIndex) => {
                    const { row, col, isStart, isEnd, isWall, isPath, isVisited } = node;
                    return (
                        <Node
                            key={`${row}${col}`}
                            width={20}
                            height={20}
                            row={row}
                            col={col}
                            isStart={isStart}
                            isEnd={isEnd}
                            isWall={isWall}
                            isPath={isPath}
                            isVisited={isVisited}
                        /> 
                    );
                })}
            </div>
        );
    })

    return (
        <>
            <div className={classes.root}>
                <div className={classes.grid}>
                    {GridMap}
                </div>
            </div>
            <Button variant="contained" color="primary" onClick={ () => animateAlgorithm() }>Animate Algorithm</Button>
            <Button variant="contained" color="primary" onClick={ () => addRandomWalls(6) }>Add Random Walls</Button>
            <Button variant="contained" color="primary" onClick={ () => reset() }>Reset</Button>
            <Button variant="contained" color="secondary" onClick={ () => logStates() }>Log Data</Button>   
        </>
    )
});

export default GridContainer

推荐阅读