【React】useImmer的使用以及与useState的对比

本文介绍了immer,一个用于处理不可变状态的第三方模块,通过ES6的proxy实现。文章对比了immer与React内置的useState在处理数组和对象状态更新时的差异,展示了immer如何简化操作过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

immer是一个第三方模块,可以更加方便的处理不可变状态,其核心实现是利用ES6的proxy。

下载

npm

npm install immer use-immer

yarn

yarn add immer use-immer

immer基本使用

语法:

produce(currentState, recipe: (draftState) => void | draftState, ?PatchListener): nextState

  • currentState: 被操作对象的最初状态
  • draftState: 根据 currentState 生成的草稿状态,它是currentState的代理,对draftState 所做的任何修改都将被记录并用于生成 nextState。在此过程中,currentState 将不受影响
  • nextState:根据draftState 生成的最终状态
  • produce: 用来生成 nextState 或 producer 的函数
  • producer: 生产者 通过 produce 生成,用来生产nextState ,每次执行相同的操作
  • recipe: 生产机器 用来操作draftState 的函数
  • PatchListener可有可无的

与useState对比

处理数组

需求:点击按钮添加一行数据

useState实现

import { useState } from "react"

function App() {
	const [list, setList] = useState([
    { id: 1, text: "aaa" },
    { id: 2, text: "bbb" },
    { id: 3, text: "ccc" },
  ])
  const handleClick = () => {
    setList([...list,{ id: 4, text: "ddd" }])
  }
  return (
  <div>
    <button onClick={handleClick}>点击添加</button>
    <ul>
        {list.map((item) => {
          return <li key={item.id}>{item.text}</li>
        })}
    </ul>
  </div>
	)
}

useImmer实现

import { useImmer } from "use-immer"

function App() {
	const [list, setList] = useImmer([
    { id: 1, text: "aaa" },
    { id: 2, text: "bbb" },
    { id: 3, text: "ccc" },
  ])
  const handleClick = () => {
    setList((draft) => {
      draft.push({ id: 4, text: "ddd" })
    })
  }
  return (
  <div>
    <button onClick={handleClick}>点击添加</button>
    <ul>
        {list.map((item) => {
          return <li key={item.id}>{item.text}</li>
        })}
    </ul>
  </div>
	)
}

处理对象

需求:点击将"小明" 变成 “大明”

useState实现

import { useState } from "react"
import { cloneDeep } from "lodash"

function App() {
	const [userInfo, setUserInfo] = useState(
  	{ 
      name:{
        firstname:"小",
        lastname:"明"
      },
      age:23
    }
  )
  const handleClick = () => {
    // 方法一:
    setUserInfo({ 
      ...userInfo,
      name:{
        ...userInfo.name,
        firstname:"大"
      }
    })
    // 方法二:也可以使用lodash
    const cloneUserInfo = cloneDeep(userInfo)
    cloneUserInfo.name.firstname = "大"
    setUserInfo(cloneUserInfo)
  }
  return (
    <div>
      <button onClick={handleClick}>修改name</button>
      <div>{ JSON.stringify(userInfo) }</div>
    </div>
	)
}

useImmer实现

import { useImmer } from "use-immer"

function App() {
	const [userInfo, setUserInfo] = useImmer(
  	{ 
      name:{
        firstname:"小",
        lastname:"明"
      },
      age:23
    }
  )
  const handleClick = () => {
    setUserInfo((draft) => {
      draft.name.firstname = "大"
    })
  }
  return (
    <div>
      <button onClick={handleClick}>修改name</button>
      <div>{ JSON.stringify(userInfo) }</div>
    </div>
	)
}
### 如何在 React 中访问和修改子组件的 `useState` 状态 #### 子组件状态提升至父组件管理 为了实现父子组件之间的状态共享,在大多数情况下,推荐的做法是将状态提升到最近的共同祖先组件中进行管理。通过回调函数的方式把子组件的状态传递给父组件。 ```jsx // ParentComponent.js import React, { useState } from 'react'; import ChildComponent from './ChildComponent'; function ParentComponent() { const [childState, setChildState] = useState('Initial State'); function handleUpdate(newState) { setChildState(newState); } return ( <div> <p>Parent Component: {childState}</p> {/* 将状态和更新方法作为属性传入 */} <ChildComponent updateState={handleUpdate} /> </div> ); } export default ParentComponent; ``` ```jsx // ChildComponent.js import React, { useState } from 'react'; function ChildComponent({ updateState }) { const [localState, setLocalState] = useState(''); function handleChange(event) { setLocalState(event.target.value); // 更新本地状态 updateState(event.target.value); // 同步通知父级组件更新其持有的状态 } return ( <input type="text" value={localState} onChange={handleChange} placeholder="Type something..." /> ); } export default ChildComponent; ``` 这种方式不仅能够保持数据流的一致性和可预测性,还使得调试更加容易[^1]。 #### 使用上下文(Context API) 对于更复杂的应用场景或当存在多层嵌套时,可以考虑利用 Context API 来提供一种无需手动层层传递 props 的方式来分享全局变量或者配置项。 创建并导出一个 context 对象: ```javascript const MyContext = React.createContext(); ``` 定义 provider 组件包裹需要接收该上下文中值的部分应用树结构,并在此处设置初始状态以及任何必要的事件处理器: ```jsx <MyContext.Provider value={{ childState, setChildState }}> ... </MyContext.Provider> ``` 最后,在消费端即目标子组件内部使用 useContext 钩子获取这些信息: ```jsx import { useContext } from 'react'; import { MyContext } from '../path/to/context'; function SomeDeeplyNestedChild() { let { childState, setChildState } = useContext(MyContext); // Now you can use these values directly within this component. } ``` 这种方法减少了 prop-drilling (props 层层穿透),提高了代码维护效率[^2].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

田本初

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值