V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
morri
V2EX  ›  问与答

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

  •  
  •   morri · 2022-08-26 19:13:53 +08:00 · 1032 次点击
    这是一个创建于 821 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    设置框的展示与否设置了通过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>
    
    8 条回复    2022-08-26 21:19:50 +08:00
    darkengine
        1
    darkengine  
       2022-08-26 19:48:12 +08:00
    先搞清楚最基本的 state 概念,setItems 都没调用怎么会有变化呢。
    morri
        2
    morri  
    OP
       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
        3
    GentleFifth  
       2022-08-26 20:23:18 +08:00 via Android
    之前是不是写 vue 的
    GPLer
        4
    GPLer  
       2022-08-26 20:36:13 +08:00   ❤️ 1
    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
        5
    darkengine  
       2022-08-26 20:42:17 +08:00
    肯定不行,要整个 Object 都换掉

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

    @GPLer 多谢回答!
    morri
        7
    morri  
    OP
       2022-08-26 20:50:09 +08:00
    @darkengine 多谢回答,这样也不行 还是要在遍历的里面使用 const [show, setShow] = React.useState(false) 才行
    okakuyang
        8
    okakuyang  
       2022-08-26 21:19:50 +08:00
    如果你用一个数组存储列表各行是否显示的状态 ,你在设置显隐的时候要传一个新的数组进去。
    例如:
    displayList[index] = true
    setDiseplayList( Array.from( displayList ) )
    数组里面是对象也是一样,你都需要传递一个新数组进去。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5573 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 07:02 · PVG 15:02 · LAX 23:02 · JFK 02:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.