当前位置: 首页 > article >正文

React-useState讲解

useState

让页面“动”起来

例如实现一个 click 计数功能,普通变量无法实现。即:修改普通变量无法触发组件的更新 rerender

通过 useState 即可实现。

state 是什么

State, A component’s memory —— 这个比喻非常好!

  • props 父组件传递过来的信息
  • state 组件自己内部的状态,不对外

每次 state 变化,都会触发组件更新,从新渲染页面。

代码演示,参考 react-ts-demo 中 pages/StateDemo1.tsx

import React, { FC, useState } from 'react'

const Demo: FC = () => {
  // let count = 0 // 普通的 js 变量,无法触发组件的更新

  const [count, setCount] = useState(0) // useState 可以触发组件的更新,
  //   const [name, setName] = useState('双越')

  function add() {
    // count++
    // setCount(count + 1)

    setCount(count => count + 1) // 使用函数,state 更新不会被合并
    setCount(count => count + 1)
    setCount(count => count + 1)
    setCount(count => count + 1)
    setCount(count => count + 1)

    // setCount(count => count + 1)
    console.log('cur count ', count) // 异步更新,无法直接拿到最新的 state 值
  }

  return (
    <div>
      <button onClick={add}>add {count}</button>
    </div>
  )
}

export default Demo

state 的特点

异步更新

代码演示

PS:setState 传入函数,可同步更新

可能会被合并

代码演示

不可变数据

state 可以是任意 JS 类型,不仅仅是值类型。

不可直接修改 state ,而要 setState 新值。

代码演示

PS:函数组件,每个更新函数从新执行,state 被重置,而不是被修改。state 可以理解为 readOnly

immer

Immer 简化了不可变数据结构的处理。特别是对于 JS 语法没那么熟悉的人。

代码演示,参考 react-ts-demo 中 pages/ImmerDemo1.tsx

import React, { FC, useState } from 'react'
import produce from 'immer'

const Demo: FC = () => {
  //   const [userInfo, setUserInfo] = useState({ name: '柚子', age: 20 })
  //   function changeAge() {
  //     // // **不可变数据** - 不去修改 state 的值,而是要传入一个新的值 —— 重要!
  //     // setUserInfo({
  //     //   ...userInfo,
  //     //   age: 21,
  //     // })

  //     setUserInfo(
  //       produce(draft => {
  //         draft.age = 21
  //       })
  //     )
  //   }

  const [list, setList] = useState(['x', 'y'])
  function addItem() {
    // // **不可变数据** - 不去修改 state 的值,而是要传入一个新的值 —— 重要!
    // setList(list.concat('z'))
    // // setList([...list, 'z'])

    setList(
      produce(draft => {
        draft.push('z')
      })
    )
  }

  return (
    <div>
      <h2>state 不可变数据</h2>
      {/* <div>{JSON.stringify(userInfo)}</div>
      <button onClick={changeAge}>change age</button> */}
      <div>{JSON.stringify(list)}</div>
      <button onClick={addItem}>add item</button>
    </div>
  )
}

export default Demo

实战:List 页面使用 state

  • 使用 state
  • 使用 immer
    • push
    • 修改 isPublish

代码参考 pages/List2.tsx

import React, { FC, useState, useEffect } from 'react'
import produce from 'immer'
import QuestionCard from './components/QuestionCard'

// 组件是一个函数(执行返回 JSX 片段),组件初次渲染执行这个函数
// 任何 state 更新,都会触发组件的更新(重新执行函数)
const List2: FC = () => {
  useEffect(() => {
    console.log('加载 ajax 网络请求')

    return () => {
      console.log('销毁')
    }
  }, []) // 无依赖,组件初次渲染时执行

  // const [count, setCount] = useState(0)
  const [questionList, setQuestionList] = useState([
    { id: 'q1', title: '问卷1', isPublished: false },
    { id: 'q2', title: '问卷2', isPublished: true },
    { id: 'q3', title: '问卷3', isPublished: false },
    { id: 'q4', title: '问卷4', isPublished: true },
  ])

  // useEffect(() => {
  //   console.log('question list changed')
  // }, [questionList])

  // useEffect(() => {
  //   console.log('count changed')
  // }, [count, questionList])

  function add() {
    // setCount(count + 1)

    const r = Math.random().toString().slice(-3)
    // setQuestionList(
    //   // 新增 concat
    //   questionList.concat({
    //     id: 'q' + r,
    //     title: '问卷' + r,
    //     isPublished: false,
    //   })
    // )

    // immer 的方式
    setQuestionList(
      produce(draft => {
        draft.push({
          id: 'q' + r,
          title: '问卷' + r,
          isPublished: false,
        })
      })
    )
  }

  function deleteQuestion(id: string) {
    // // 不可变数据
    // setQuestionList(
    //   // 删除 filter
    //   questionList.filter(q => {
    //     if (q.id === id) return false
    //     else return true
    //   })
    // )

    // immer 的方式
    setQuestionList(
      produce(draft => {
        const index = draft.findIndex(q => q.id === id)
        draft.splice(index, 1)
      })
    )
  }

  function publishQuestion(id: string) {
    // setQuestionList(
    //   // 修改 map
    //   questionList.map(q => {
    //     if (q.id !== id) return q

    //     return {
    //       ...q,
    //       isPublished: true,
    //     }
    //   })
    // )

    // immer 的方式
    setQuestionList(
      produce(draft => {
        const q = draft.find(item => item.id === id)
        if (q) q.isPublished = true
      })
    )
  }

  return (
    <div>
      <h1>问卷列表页2</h1>
      <div>
        {questionList.map(question => {
          const { id, title, isPublished } = question
          return (
            <QuestionCard
              key={id}
              id={id}
              title={title}
              isPublished={isPublished}
              deleteQuestion={deleteQuestion}
              publishQuestion={publishQuestion}
            />
          )
        })}
      </div>
      <div>
        <button onClick={add}>新增问卷</button>
      </div>
    </div>
  )
}

export default List2


代码参考 /components/QuestionCard

import React, { FC, useEffect } from 'react'
import classnames from 'classnames'
// import './QuestionCard.css'
import styles from './QuestionCard.module.scss'

// ts 自定义类型
type PropsType = {
  id: string
  title: string
  isPublished: boolean
  deleteQuestion?: (id: string) => void
  publishQuestion?: (id: string) => void
}

// FC - functional component
const QuestionCard: FC<PropsType> = props => {
  const { id, title, isPublished, deleteQuestion, publishQuestion } = props

  function publish(id: string) {
    publishQuestion && publishQuestion(id)
  }

  function del(id: string) {
    deleteQuestion && deleteQuestion(id)
  }

  // useEffect(() => {
  //   console.log('question card mounted')

  //   return () => {
  //     console.log('question card unmounted', id) // 销毁
  //   }

  //   // 生命周期:创建,更新(state 变化),销毁
  // }, [])

  // let itemClassName = 'list-item'
  // if (isPublished) itemClassName += ' published'
  // // 逻辑稍微复杂

  // const itemClassName = classnames('list-item', { published: isPublished })
  // const itemClassName = classnames({
  //   'list-item': true,
  //   published: isPublished,
  // })

  const listItemClass = styles['list-item']
  const publishedClass = styles.published
  const itemClassName = classnames({
    [listItemClass]: true,
    [publishedClass]: isPublished,
  })

  return (
    <div key={id} className={itemClassName}>
      <strong>{title}</strong>
      &nbsp;
      {/* 条件判断 */}
      {isPublished ? <span className={styles['published-span']}>已发布</span> : <span>未发布</span>}
      &nbsp;
      <button
        onClick={() => {
          publish(id)
        }}
      >
        发布问卷
      </button>
      &nbsp;
      <button
        onClick={() => {
          del(id)
        }}
      >
        删除问卷
      </button>
    </div>
  )
}

export default QuestionCard

最重要的就是:不可变数据 —— 这是 React state 的核心


http://www.kler.cn/a/504905.html

相关文章:

  • 音频语言模型与多模态体系结构
  • idea分支合并代码
  • STM32 FreeRTOS 的任务挂起与恢复以及查看任务状态
  • 【优选算法篇】:分而治之--揭秘分治算法的魅力与实战应用
  • Git使用笔记
  • 多模态论文笔记——CLIP
  • 软考信安22~网站安全需求分析与安全保护工程
  • CCLINKIE转ModbusTCP网关,助机器人“掀起”工业智能的“惊涛骇浪”
  • 如何运行Pytest(python -m pytest 与 pytest详解)
  • 网络精英赛模拟练习
  • Oracle 学习全攻略
  • ElasticSearch-Nested 类型与 Object 类型的区别
  • 交叉编译avahi到aarch64平台
  • iOS - 内存屏障的使用场景
  • 【机器学习】实战:天池工业蒸汽量项目(三)模型预测
  • 速通Docker === 介绍与安装
  • Graylog采集MySQL慢日志实战
  • 源码编译安装httpd 2.4,提供系统服务管理脚本并测试
  • jenkins docker 遇到 /var/run/docker.sock: permission denied 解决方案
  • 【JavaEE进阶】SpringMVC 响应
  • 代码随想录算法训练营第三十四天-动态规划-63. 不同路径II
  • vue运用uniapp框架开发企业微信小程序中常用的一些基础方法
  • C#,入门教程(27)——应用程序(Application)的基础知识
  • JavaScript:模版字符串
  • scons通用构建_生成方法Command
  • 向量数据库如何助力Text2SQL处理高基数类别数据