React 不会在数组状态更新时重新渲染

Jan*_*ong 6 reactjs material-ui react-hooks

我正在尝试制作 3 个按钮控件来打开和关闭 3 个相应的菜单列表。Material-UI是我目前使用的UI框架。这是代码:


const Filters = () => {

    const [anchors, setAnchors] = useState([null, null, null])

    const handleClick = (event, index) => {
        const arr = anchors
        arr[index] = event.target
        setAnchors(arr)    
    }

    const handleClose = (index) => {
        const arr = anchors
        arr[index] = null
        setAnchors(arr)  
    }

    return(
        <div id="top-filters">
            <div className="controls">
                <Button onClick={(event) => handleClick(event, 0)}>Sort By</Button>
                <Button onClick={(event) => handleClick(event, 1)}>Price</Button>
                <Button onClick={(event) => handleClick(event, 2)}>Delivery Fees</Button>
            </div>
        {
            console.log({anchors})
        }
            <Menu key={1}
                anchorEl={anchors[0]} 
                open={Boolean(anchors[0])} 
                onClose={() => handleClose(0)} 
                keepMounted
            >
                <MenuItem>
                    <ListItemText primary="By Rating" />
                </MenuItem>
            </Menu>

            <Menu key={2}
                anchorEl={anchors[1]} 
                open={Boolean(anchors[1])} 
                onClose={() => handleClose(1)} 
                keepMounted            
            >
                <MenuItem>
                    <Slider defaultValue={25} marks={marks} step={25} />
                </MenuItem>
            </Menu>

            <Menu key={3}
                anchorEl={anchors[2]} 
                open={Boolean(anchors[2])} 
                onClose={() => handleClose(2)} 
                keepMounted            
            >
                <MenuItem>
                    <ListItemText primary="By Delivery Fees" />
                </MenuItem>
            </Menu>

            <FormControl className="search-bar-filter">
                <Icon>search</Icon>
                <StyledInput classes={{ root: classes.root }} name="search" type="search" placeholder="Search" disableUnderline />
            </FormControl>
        </div>
    )
}

export default Filters
Run Code Online (Sandbox Code Playgroud)

我检查了控制台中的值更新,它们看起来很好,但我不确定为什么 React 不会重新渲染页面(当我单击按钮时,没有任何反应,但锚点状态已更新)。感谢帮助。

dev*_*kan 12

你正在做的事情是一个突变:

arr[index] = event.target
Run Code Online (Sandbox Code Playgroud)

更新状态时应该避免突变。因为如果你改变你的状态 React 就无法理解你的状态已经改变。您可以使用类似 的方法mapfilter或任何其他不会改变您的状态的方法。

const handleClick = (event, index) => {
  setAnchors((prev) =>
    prev.map((el, i) => {
      if (i !== index) {
        return el;
      }

      return event.target;
    })
  );
};
Run Code Online (Sandbox Code Playgroud)

或者如果你喜欢简洁的:

const handleClick = (event, index) => {
  setAnchors((prev) => prev.map((el, i) => (i !== index ? el : event.target)));
};
Run Code Online (Sandbox Code Playgroud)

扩展语法可用于非嵌套数组,但如果您正在处理嵌套数组并更改嵌套值,请务必小心,因为扩展语法会创建浅副本。map这就是为什么我大多数时候喜欢使用方法。

如果您不想映射整个数组(有些人更喜欢这样做)Object.assign和扩展语法可以一起使用。

const handleClick = (event, index) => {
  setAnchors((prev) => Object.assign([], { ...prev, [index]: event.target }));
};
Run Code Online (Sandbox Code Playgroud)

更新:

正如我之前所解释的,扩展语法仅进行浅复制。

注意:复制数组时,扩展语法实际上会深入一层。因此,它可能不适合复制多维数组,如以下示例所示。(Object.assign() 和扩展语法也是如此。)

来源

这意味着嵌套值保留与原始值相同的引用。因此,如果您更改新数组(或对象)的某些内容,它也会更改原始数组(或对象)。

arr[index] = event.target
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我们的原始数组也发生了变化。实际上,这不是嵌套数组,但我们正在更改数组元素的嵌套属性。也许这是嵌套数组的一个更好的例子,但上面的例子更现实。

const handleClick = (event, index) => {
  setAnchors((prev) =>
    prev.map((el, i) => {
      if (i !== index) {
        return el;
      }

      return event.target;
    })
  );
};
Run Code Online (Sandbox Code Playgroud)


wan*_*v87 7

您错误地更新了状态。 const arr = anchors不是克隆锚点的正确方法。您需要使用...运算符。

    const handleClick = (event, index) => {
        const arr = [... anchors]
        arr[index] = event.target
        setAnchors(arr)    
    }

    const handleClose = (index) => {
        const arr = [... anchors]
        arr[index] = null
        setAnchors(arr)  
    }
Run Code Online (Sandbox Code Playgroud)