【React】较为复杂的state | state arrays | state objects

202 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

本文主要内容:承接上一篇文章对于 useStatesetState 的总结,进一步总结如何对较为复杂的state进行操作。

其实在我看来,useState 的核心就是拆解赋值,将方法中的初始值赋给数组中的第一个元素,将处理这个值的函数赋给数组中的第二个元素;setState 的核心是每次通过一个函数更新state的值,无论是不考虑先前的值还是考虑先前的值,本质上都是进行再一次的赋值。所以复杂的state也仅仅只是复杂在类型上,思想上是一定一致的。

数组 state

预览与分析

image.png

我们要实现每一次点击 Add Item 按钮,都会在下方新增一个 Thing ,并且它后面的数字也要同步增长。

静态网页实现

直接定义一个数组,然后利用 map 方法解析成为 数组元素带有标签的数组 ,再渲染到页面上即可

所以可以直接看代码如下:

import React from 'react';

export default function App() {
    const thingsArray = ["Thing 1", "Thing 2"]
    const thingsElements = thingsArray.map(thing => <p key={thing}>{thing}</p>)
    
    return (
        <div>
            <button>Add Item</button>
            {thingsElements}
        </div>
    )
}

使用useState和setState

根据前言中说到,先用 useState 进行初始化的解析赋值,如下

const [thingsArray, setThingsArray] = React.useState(["Thing 1", "Thing 2"])

接下来在button处添加一个鼠标事件,在这个事件函数中使用setState更新数组

所以最终可以这样子写:

import React from 'react';

export default function App() {
    const [thingsArray, setThingsArray] = React.useState(["Thing 1", "Thing 2"])
    
    function addItem() {
        setThingsArray(prevThingsArray => {
            return [...prevThingsArray, `Thing ${prevThingsArray.length + 1}`]
        })
    }
    
    const thingsElements = thingsArray.map(thing => <p key={thing}>{thing}</p>)
    
    return (
        <div>
            <button onClick={addItem}>Add Item</button>
            {thingsElements}
        </div>
    )
}

在这里需要使用考虑先前的值的 setState ,每次接收先前的一个数组,再进行解析拆分后放入一个新的数组,同时放入的还有要添加的值

这样将新产生的数组更新掉先前的数组,就能实现 Add Item 的功能

gsr1m-j4nfs.gif

对象 state

预览与分析

image.png

这是一张简易的小名片,我们要做的是实现点击那颗星星,然后星星会被填上表示选中

静态网页实现

这里就直接写useState后的静态网页代码了

import React from "react"

export default function App() {
    const [contact, setContact] = React.useState({
        firstName: "heheer",
        lastName: "WANG",
        qq: "1239331448",
        email: "1239331448@qq.com",
        isFavorite: false
    })

    return (
        <main>
            <article className="card">
                <img src="./images/user.png" className="card--image" />
                <div className="card--info">
                    <img 
                        src={`./images/star.png`} 
                        className="card--favorite"
                        onClick={toggleFavorite}
                    />
                    <h2 className="card--name">
                        {contact.firstName} {contact.lastName}
                    </h2>
                    <p className="card--contact">qq: {contact.qq}</p>
                    <p className="card--contact">email: {contact.email}</p>
                </div>
                
            </article>
        </main>
    )
}

可以看到定义了一个对象 contact ,通过访问对象的属性将不同的值渲染到了页面上

我们接下来的目标是通过 setState 修改对象 isFavorite 属性,使得其能通过点击进行 truefalse 的互换,并且使用三元表达式来选择 imgsrc ,进而修改星星的样式

实现三元表达式

为了使看起来更直观,我们先写 isFavoritestar 对应的三元表达式,如下

let starIcon = contact.isFavorite ? "star-fill.png" : "star.png"

所以再对 imgsrc 进行修改

src={`./images/${starIcon}`} 

就可以实现简单的对应了

实现对象的setState

接下来就可以在预留的事件函数 toggleFavorite 中使用 setState 实现功能了

所以我们进行如下修改

function toggleFavorite() {
  setContact(prevContact => {
    return {
        isFavorite: !prevContact.isFavorite
    }
  })
}

接收先前对象的值,将 isFavorite 的值取反,从而实现布尔值的互换

然而实际情况却是这样的

qw0pd-1hybl.gif

实现了星星的互换确实不假,但是其他的对象属性就直接消失了,这非常不好

原因在于最后只是重新将isFavorite的值重新赋给了对象,而其他属性没能收到值,理所当然的值为空,自然就无法渲染出东西来

所以我们应该同时将所有属性的值重新赋予,再在这个基础上修改 isFavorite,这里可以将对象的每个属性一一列出,不过还是用 ...展开运算符比较方便,最终代码如下

function toggleFavorite() {
  setContact(prevContact => {
    return {
      ...prevContact,
      isFavorite: !prevContact.isFavorite
    }
  })
}

实现结果如下

kgg41-vgb9q.gif