setState 不会更新 React 中的子级
Posted
技术标签:
【中文标题】setState 不会更新 React 中的子级【英文标题】:setState does not update children in React 【发布时间】:2022-01-16 15:13:05 【问题描述】:我正在构建一个简单的应用程序,以直观地构建要使用不同搜索算法解决的迷宫。我有一个状态maze
,其中包含每个块的状态数组,我将它传递给一个子Grid
,它从数组中渲染迷宫。但是,每当我使用函数 updateMaze(y, x, newState)
更新状态时,maze
状态就会更新,但子 Grid
不会重新渲染。这是为什么呢?
App.js:
import './App.css';
import Block from './components/Block'
import Row from './components/Row'
import Grid from './components/Grid'
import MazeView from './components/MazeView';
import SideBarItem from './components/SideBarItem';
import New from './assets/new-page.png'
import Checkmark from './assets/checkmark.png'
import Maximize from './assets/maximize.png'
import Random from './assets/random.png'
import Square from './assets/square-measument.png'
import useState, useEffect from 'react'
const useForceUpdate = () =>
const [count, setCount] = useState(0)
const increment = () => setCount(prevCount => prevCount + 1)
return [increment, count]
function App()
const [forceUpdate] = useForceUpdate()
const [size, setSize] = useState(
width: 15,
height: 8
)
const defaultDimensions = 85
const [dimensions, setDimensions] = useState(defaultDimensions)
const [scale, setScale] = useState(1)
const [MazeViewStyle, setMazeViewStyle] = useState(String())
const [maze, setMaze] = useState([])
const [globalW, globalH] = [window.innerWidth * 0.9 - 35, window.innerHeight * 0.85]
const getAttrib = (columns, rows, defaultDimensions) =>
let scale = defaultDimensions
// If there are more columns than rows
if (columns >= rows)
// Sets the scale to fill the height with rows
scale = globalH / (rows * defaultDimensions)
// Unless the columns do not fill the entire width of the screen
if (columns * defaultDimensions * scale < globalW)
scale = globalW / (columns * defaultDimensions)
// In the opposite scenario (rows more than columns)
if (rows > columns)
// Sets the scale to fill the width with columns
scale = globalW / (columns * defaultDimensions)
// Unless the rows do not fill the height
if (rows * defaultDimensions * scale < globalH)
scale = globalH / (rows * defaultDimensions)
// Compute flags
const flags =
centerWidth: columns * defaultDimensions < globalW,
centerHeight: rows * defaultDimensions < globalH
// Sets maximum result 1 and minimum 0
if (scale >= 1) return scale: 1, flags: flags
else if (scale <= 0.1) return scale: 0.1, flags: centerWidth: false, centerHeight: false
else return scale: scale, flags: centerWidth: false, centerHeight: false
const getMazeViewAuxStyle = (flags) =>
// Unpack a flag
let [centerWidth, centerHeight] = [flags.centerWidth, flags.centerHeight]
// If both flags are false return an empty string
if (!centerWidth && !centerHeight) return String()
// If the columns and rows are not sufficient
if (dimensions * size.width < globalW && dimensions * size.height < globalH) return "small smallw smallh"
// Otherwise find the necessary class names
let style = "small"
if (centerWidth) style = style + " smallw"
if (centerHeight) style = style + " smallh"
return style
const populateArea = () =>
// Fetch attributes of the current maze
const fetchedAttrib = getAttrib(size.width, size.height, defaultDimensions)
// Update the scale and dimensions
setScale(fetchedAttrib.scale)
setDimensions(defaultDimensions * fetchedAttrib.scale)
// Update flags
setMazeViewStyle(["maze-view", getMazeViewAuxStyle(fetchedAttrib.flags)].join(" "))
// Initialize maze space
initializeMazeSpace(size.height, size.width)
populateRandom()
// renderMaze()
// Populates the maze in the right dimensions
// only when a new maze is loaded
useEffect(() =>
populateArea()
, [true])
// Updates the dimensions based on scale
useEffect (() =>
setDimensions(defaultDimensions * scale)
, [scale])
const initializeMazeSpace = (rows, columns) =>
let newMaze = maze
for (let i = 0; i < rows; i++)
newMaze[i] = []
for (let j = 0; j < columns; j++)
newMaze[i][j] = "empty"
setMaze(newMaze)
const updateMaze = (i, j, blockState) =>
if (maze.length === 0)
initializeMazeSpace(size.height, size.width)
setMaze(() =>
maze.map((row, a) =>
i === a ? (row.map((block, b) => b === j ? blockState : block)) : row
)
)
const populateRandom = (height = size.height, width = size.width) =>
let newMaze = maze
const classes = ["empty", "wall", "steel"]
for (let i = 0; i < height; i++)
for (let j = 0; j < width; j++)
let pick = classes[Math.floor(Math.random() * 3)]
newMaze[i][j] = pick
setMaze(newMaze)
const renderMaze = () =>
console.log(maze)
let grid = []
for (let i = 0; i < maze.length; i++)
let row = []
for (let j = 0; j < size.width; j++)
row.push(<Block inheritedType=maze[i][j] dimensions=defaultDimensions * scale />)
grid.push(<Row columns=row/>)
return grid
// const renderMaze = () =>
// let mazeComponents = maze.map((row) =>
// return <Row columns=row.map(block => (<Block inheritedType=block dimensions=defaultDimensions * scale onAction=() => console.log("running") onDoubleClick=(e, p) => e.target.classList.remove(p)/>))/>
// )
// return mazeComponents
//
return (
<>
<div className='view'>
<MazeView style=MazeViewStyle grid=<Grid rows=renderMaze() />/>
<div className='sidebar'>
<SideBarItem icon=New onClick=() =>
updateMaze(0,0,"steel")
/>
<SideBarItem icon=Square onClick=() => console.log(maze)/>
<SideBarItem icon=Maximize onClick=() => setScale(0.5) />
<SideBarItem icon=Random onClick=() => populateRandom()/>
<SideBarItem icon=Checkmark />
</div>
</div>
</>
);
export default App
Grid.js:
import React from 'react'
import Row from './Row'
import Block from './Block'
const Grid = ( rows ) =>
return (
<div className='grid-view'>
rows
</div>
)
export default Grid
注意:setScale 会触发重新渲染。
【问题讨论】:
【参考方案1】:我认为你的 initializeMazeSpace 函数有问题,
let newMaze = maze
引用了同一个数组。因此,您的状态会发生变异,然后与自身进行比较,因此比较的结果不会改变,并且不会触发重新渲染操作。如果你想复制一个状态,试试这个
let newMaze = [...maze]
【讨论】:
当我这样做并相应地调整 setMaze 函数时,populateRandom 为 Maze 获取了一个空数组,这是为什么呢? initializeMazeSpace() 到目前为止工作正常 哦,对不起。也许问题出在 renderMaze 上,您应该为网格数组创建一个状态,然后将其作为道具下推到 Grid 组件。如果你只是推送一个函数,我认为它只会渲染 1 次 成功了。问题(?)原来是在以上是关于setState 不会更新 React 中的子级的主要内容,如果未能解决你的问题,请参考以下文章
Quasar(vue)中嵌套在路由器中的子级中的父级触发方法