React 遍历数组中的元素,渲染出来的对象如何控制子项的展示或隐藏呢?

2022-08-26 19:13:53 +08:00
 morri

想要实现 点击每个项的设置,展示对应的设置框。

设置框的展示与否设置了通过show属性来判断

<div className={i.show ? 'menu-show' : 'menu-hide'}>
 <p>名称: {i.name}</p>
 <p>年龄: <input type="text"/></p>
</div>

点击 box 框时 也设置了 <div key={i.name} className="box" onClick={() => i.show = !i.show}>

可为什么没效果呢? 下面是 demo 代码。

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        .wrapper, h1 {
            display: flex;
            margin: auto 220px;
        }

        .box {
            font-size: 16px;
            background-color: #00a0e9;
            margin: 3px;
            border-radius: 3px;
            padding: 3px;
        }

        .menu-hide, .menu-show {
            margin-top: 12px;
        }

        .menu-hide {
            display: none;
        }

        .menu-show {
            display: block;
        }
    </style>

</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
    const App = () => {
        const [items, setItems] = React.useState([{name: 'twitch', age: 3}, {name: '虎牙', age: 5}, {name: '斗鱼', age: 6}])
        return <div>
            <h1>study</h1>
            <div className="wrapper">
                {items.map(i => {
                    i.show = false
                    return <div key={i.name} className="box" onClick={() => i.show = !i.show}>
                        <p>名称 {i.name}</p>
                        <p>年龄: {i.age}</p>
                        <a href="#">设置</a>
                        <div className={i.show ? 'menu-show' : 'menu-hide'}>
                            <p>名称: {i.name}</p>
                            <p>年龄: <input type="text"/></p>
                        </div>
                    </div>
                })}
            </div>
        </div>
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<App/>)
</script>
</html>
909 次点击
所在节点    问与答
8 条回复
darkengine
2022-08-26 19:48:12 +08:00
先搞清楚最基本的 state 概念,setItems 都没调用怎么会有变化呢。
morri
2022-08-26 20:13:16 +08:00
我试过 有调用 ` setItems(items[index].show = true)` 这样也不行。
@darkengine
<div className="wrapper">
{items.map((i, index) => {
i.show = false
return <div key={i.name} className="box" onClick={() => {
setItems(items[index].show = true)
}}>
<p>名称 {i.name}</p>
<p>年龄: {i.age}</p>
<a href="#">设置</a>
<div className={i.show ? 'menu-show' : 'menu-hide'}>
<p>名称: {i.name}</p>
<p>年龄: <input type="text"/></p>
</div>
</div>
})}
</div>



但是 在 telegram 群里面问的哥哥 这样改下就可以了 在遍历里面 再使用 ` const [show, setShow] = React.useState(false);`

```

<div className="wrapper">
{items.map(i => {
const [show, setShow] = React.useState(false);
return <div key={i.name} className="box" onClick={() => setShow(v => !v)}>
<p>名称 {i.name}</p>
<p>年龄: {i.age}</p>
<a href="#">设置</a>
<div className={show ? 'menu-show' : 'menu-hide'}>
<p>名称: {i.name}</p>
<p>年龄: <input type="text"/></p>
</div>
</div>
})}
</div>

```
好奇为什么要这样用才行。
GentleFifth
2022-08-26 20:23:18 +08:00
之前是不是写 vue 的
GPLer
2022-08-26 20:36:13 +08:00
1. React 在更新的时候只会对 State 进行**浅比较**,对于数组对象这样的引用类型,光修改内部值,React 无法知道其发生变化,这个 Vue2 其实也是这样的,要解决这个问题,最简单的办法就是 setItems(items.slice()) 创建一个新的数组并赋值,让 React 知道发生了变化.
2. 你一开始的代码,像 i.show = false 这样的初始化语句不能写在这,return 的 JSX 可以视作渲染函数,每次都会被调用,相当于每次都会执行,意味着 show 一直都为 false ,这个如果真的不方便直接写在数组里,可以用 React.useEffect 套一下。
3. 为什么你后面发的代码再写一个 State 可以解决,因为这个 State 内的数据类型是简单的数据类型,直接变化是 React 可以检测到的,所以 React 会重新渲染,但这样只解决了更新的问题,你的目的是取反,这样做只能让值变 true ,不能变 false ,因为页面更新后值又变成了 false 。
4. 所以基于你修改后的版本,建议将
```
const [show, setShow] = React.useState(false);
```
改为
```
const [show, setShow] = React.useState(i.show || false);
```
并将
```
onClick={() => setShow(v => !v)}
```
改为
```
onClick={() => { i.show = !i.show;setShow(i.show); }}
```
5. 本人是写 Vue 的,以上的回复不保证其正确性(
darkengine
2022-08-26 20:42:17 +08:00
肯定不行,要整个 Object 都换掉

items[index].show = true
setItems([...items])
morri
2022-08-26 20:42:29 +08:00
@GentleFifth 写过

@GPLer 多谢回答!
morri
2022-08-26 20:50:09 +08:00
@darkengine 多谢回答,这样也不行 还是要在遍历的里面使用 const [show, setShow] = React.useState(false) 才行
okakuyang
2022-08-26 21:19:50 +08:00
如果你用一个数组存储列表各行是否显示的状态 ,你在设置显隐的时候要传一个新的数组进去。
例如:
displayList[index] = true
setDiseplayList( Array.from( displayList ) )
数组里面是对象也是一样,你都需要传递一个新数组进去。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/875678

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX