放弃 useState 和 useReducer:为什么 useImmer 是更好的选择
在 React 中,useState和useReducer通常用于管理状态。虽然useImmer是 的流行替代方案useState,但在本文中,我们将探讨如何将其作为 的更简单、更有效的替代方案useReducer。
目录
如果你不熟悉 useImmer
useImmer是一个自定义的 React Hook。它与 类似,useState但具有一些独特的优势,尤其是在管理复杂状态时。使用useImmer,您可以像常规 JavaScript 一样直接更新状态,就像它是直接可变的一样。这是可能的,因为在后台useImmer使用该Immer库创建了状态的新的不可变副本。
要使用useImmer,首先,您需要通过在终端中运行以下命令来安装它:
npm install immer use-immer
一个简单的例子useImmer:
/* importing useImmer */
import { useImmer } from 'use-immer';
/* functional component */
function Count() {
  /* Declaring a new state variable using the useImmer hook.
    Here, we are initializing our state with a single
    property called "count" with an initial value of 0 */
  const [state, updateState] = useImmer({ count: 0 });
  /* Defining a function that will modify our state */
  const increment = () => {
    /* calling the updateState function and passing as
       it a draft */
    updateState(draft => {
      /* Here we are directly modifying the draft as 
        if it were mutable */
      draft.count += 1;
    })
  }
  /* Defining another function that will modify our state */
  const decrement = () => {
    /* Calling the updateState function and pass it a draft */
    updateState(draft => {
      /*Here we are directly modifying the draft as 
        if it were mutable */
      draft.count -= 1;
    })
  }
  /* Rendering the UI using the state */
  return (
    <div>
      <h3>Count: {state.count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  )
}
为什么我们应该使用 useImmer 而不是 useReducer
我们将探索一个useImmer遵循该useReducer模式的 示例。我们将创建状态和动作,并以与 类似的方式使用它们useReducer,但 useImmer 具有更高的可读性、灵活性和可变性。通过这个例子,我们将了解为什么useImmer应该比 更受重视useReducer。
导入 useImmer 并创建函数组件
 
// import useImmer
import { useImmer } from 'use-immer';
// functional component
function Cart() {
  // ... 
}
定义初始状态
使用 时useReducer,通常的做法是在继续操作之前定义初始状态。同样,我们将在useImmerhook 中使用这种方法。为此,我们将创建一个稍微复杂一些的状态,类似于您通常使用 定义的状态useReducer。
// initial state
const initialState = {
    items: [],
    shippingAddress: {
        street: '',
        city: ''
    }
}  
创建国家
我们已经定义了初始状态。现在,我们可以使用钩子来创建状态useImmer。
// Creating the state
const [cart, updateCart] = useImmer(initialState)
该useImmer钩子返回一个包含两个值的数组:当前状态(cart在此示例中)和我们可以用来更新状态的函数(updateCart在此示例中)。
创建动作
使用useReducer钩子时,必须定义操作才能更新状态。同样,我们将创建操作来useImmer模拟相同的模式,并实现可预测的状态更新。
const actions = {
    // Add an item to the cart
    addItemToCart: (payload) => {
        const { item } = payload
        updateCart(draft => {
            draft.items.push(item)
        });
    },
    // Remove an item from the cart
    removeItemFromCart: (payload) => {
        const { itemIndex } = payload
        updateCart(draft => {
            draft.items.splice(itemIndex, 1)
        })
    },
    // Update the shipping address of the cart
    updateShippingAddress: (payload) => {
        const { shippingAddress } = payload
        updateCart(draft => {
            draft.shippingAddress = shippingAddress
        })
    }
}
使用上述方法定义动作有两个优点useReducer:
⚡️ 可变性:有了useImmer,我们拥有了可变更新的强大功能,可以减少代码量,并使其更像 JavaScript。这与useReducer需要更函数式编程方法和不可变更新的 形成了对比。通过在 中使用可变更新useImmer,您可以用更少的代码行实现相同的结果,从而更易于编写和维护。
⚡️ 可读性:与 相比useReducer,其中动作通常定义为switchreducer 函数中的案例,而该useImmer方法更具可读性,因为每个动作都是一个具有清晰简洁名称的单独函数。
使用 JSX 渲染 UI
最后,我们可以使用JSX 中的cart状态和actions对象来渲染 UI。
<div>
    {/* Displaying the changes of the 'cart' state */}
    <p>Cart State: {JSON.stringify(cart)}</p>
    {/* Display a list of items in the cart */}
    <ul>
        {cart.items.map((item, index) => (
            <li key={index}>
                {item.name} - ${item.price}
                {/* Call the removeItemFromCart action when the remove button is clicked */}
                <button onClick={() => actions.removeItemFromCart({ itemIndex: index })}>Remove</button>
            </li>
        ))}
    </ul>
    {/* Call the addItemToCart action when the add item button is clicked */}
    <button onClick={() => actions.addItemToCart({ item: { name: 'Product', price: 9.99 } })}>
        Add Item
    </button>
    {/* Allow the user to update the shipping address */}
    <div>
        <h4>Shipping Address:</h4>
        <input type="text" placeholder="Street"
            value={cart.shippingAddress.street}
            onChange={(e) => actions.updateShippingAddress({ shippingAddress: { ...cart.shippingAddress, street: e.target.value } })}
        />
        <input type="text" placeholder="City"
            value={cart.shippingAddress.city}
            onChange={(e) => actions.updateShippingAddress({ shippingAddress: { ...cart.shippingAddress, city: e.target.value } })}
        />
    </div>
</div>
使用 useImmer 优于 useReducer
在本节中,我们已经探讨了 useImmer 相对于 useReducer 的两个优势(可变性和可读性)Create Actions。然而,还有一个更重要的优势值得讨论,我们将在本节中介绍。
⚡️ 灵活性:使用 useImmer,可以在定义的操作之外更新状态对象。这在 useReducer 中是无法实现的,并且在需要更灵活地更新状态的某些情况下尤其有用。
 
 以下是如何useImmer在不定义新操作的情况下清空购物车的示例:
<button onClick={() => updateCart(initialState)}>Clear Cart</button>
此按钮组件会将cart状态重置回其初始状态,并清空购物车中的所有商品。使用useImmer,我们可以以这种临时方式直接更新状态对象,而无需定义任何操作。这在 中是不可能的useReducer,因为所有状态更新都必须通过已定义好的操作来调度。
完整代码
 
// import useImmer
import { useImmer } from 'use-immer';
// functional component
export default function Cart() {
    // Define the initial state of the cart
    const initialState = {
        items: [],
        shippingAddress: {
            street: '',
            city: ''
        }
    };
    // Call the useImmer hook to create a cart state 
    const [cart, updateCart] = useImmer(initialState);
    // Define a set of actions that can be used to update the cart state
    const actions = {
        // Add an item to the cart
        addItemToCart: (payload) => {
            const { item } = payload
            updateCart(draft => {
                draft.items.push(item)
            });
        },
        // Remove an item from the cart
        removeItemFromCart: (payload) => {
            const { itemIndex } = payload
            updateCart(draft => {
                draft.items.splice(itemIndex, 1)
            })
        },
        // Update the shipping address of the cart
        updateShippingAddress: (payload) => {
            const { shippingAddress } = payload
            updateCart(draft => {
                draft.shippingAddress = shippingAddress
            })
        }
    }
    // Render the cart UI
    return (
        <div>
            {/* Displaying the changes of the 'cart' state */}
            <p>Cart State: {JSON.stringify(cart)}</p>
            {/* Display a list of items in the cart */}
            <ul>
                {cart.items.map((item, index) => (
                    <li key={index}>
                        {item.name} - ${item.price}
                        {/* Call the removeItemFromCart action when the remove button is clicked */}
                        <button onClick={() => actions.removeItemFromCart({ itemIndex: index })}>Remove</button>
                    </li>
                ))}
            </ul>
            {/* Call the addItemToCart action when the add item button is clicked */}
            <button onClick={() => actions.addItemToCart({ item: { name: 'Product', price: 9.99 } })}>
                Add Item
            </button>
            {/* Allow the user to update the shipping address */}
            <div>
                <h4>Shipping Address:</h4>
                <input type="text" placeholder="Street"
                    value={cart.shippingAddress.street}
                    onChange={(e) => actions.updateShippingAddress({ shippingAddress: { ...cart.shippingAddress, street: e.target.value } })}
                />
                <input type="text" placeholder="City"
                    value={cart.shippingAddress.city}
                    onChange={(e) => actions.updateShippingAddress({ shippingAddress: { ...cart.shippingAddress, city: e.target.value } })}
                />
            </div>
            {/* Call the updateCart function with the initial state to clear the cart */}
            <button onClick={() => updateCart(initialState)}>Clear Cart</button>
        </div>
    )
}
我们什么时候应该使用 useImmer?
🧨 用于useImmer本地状态管理和创建可重用组件
当管理本地状态或创建需要状态管理的可重用组件时,
useImmer是一个合适的选择。
🧨 我们可以使用useImmer全局状态管理,但还有其他更好的选择
我们可以使用和
useImmer与其他组件共享 由 创建的状态。因此,我们可以使用来创建全局状态,但是我们应该使用它来进行全局状态管理吗?createContextuseContextuseImmer当谈到全局状态管理时,像
redux toolkit或zustand这样的库更适合。
🧨 尽管其他库可能更适合全局状态管理,但 useImmer 是本地状态管理和创建可重用组件的更好选择
redux像和 这样的库zustand是为全局状态管理而创建的。如果我们用它们来创建可复用的组件:
- 由于多个组件可以修改相同的状态,因此状态将受到污染,从而导致意外行为和错误。
另一方面,如果我们使用
useImmer可重用组件:
组件的每个实例都有其独立的状态,与同一组件的其他实例相互独立。这确保了一个实例的状态不会影响另一个实例的状态。
由于状态包含在组件内,因此无需担心状态在应用程序的其他地方被修改。
🧨 在其他组件之间共享 useImmer 创建状态的方法
对于全局状态
- 我们可以使用
createContext和useContext在其他组件之间共享状态。本地状态和可重用组件:
- 假设我们在组件中使用本地状态或创建一个可复用组件。该组件变得越来越大,因此我们将其拆分为多个子组件。现在,在父组件中,我们将使用 创建状态
useImmer。然后,我们将通过 props 与子组件共享状态。
就这样。😃 感谢阅读。🎉
鏂囩珷鏉ユ簮锛�https://dev.to/rasaf_ibrahim/ditching-usestate-and-usereducer-why-useimmer-is-the-better-option-11hk 后端开发教程 - Java、Spring Boot 实战 - msg200.com
            后端开发教程 - Java、Spring Boot 实战 - msg200.com