React 状态管理破解

2025-06-07

React 状态管理破解

每个应用程序都需要某种状态管理。让我们从最基本的开始,看看随着规模的扩大,情况会如何变化。

2.1 创建基本的全局商店

这里的想法是有一个useState可以存储我们的状态并更新它的东西,然后我们将使用反应上下文将它传递给组件。

所以现在我们将创建一个名为的新上下文StoreContext,其值中第一个项目将是商店本身,第二个项目将是 setStore,以便我们可以更新它。

import React, { createContext, useContext, useMemo, useState } from 'react'

const StoreContext = createContext()

export const StoreProvider = ({ children, initialState }) => {
  const [store, setStore] = useState(initialState)

  const contextValue = useMemo(() => [store, setStore], [store])

  return (
    <StoreContext.Provider value={contextValue}>
      {children}
    </StoreContext.Provider>
  )
}

export const useStore = () => {
  return useContext(StoreContext)
}

export default StoreContext
Enter fullscreen mode Exit fullscreen mode

2.2 有些事情似乎不对劲

你的商店能容纳的数据量是有限的useState,用 setStore 更新商店最终会变成一个 PIA。所以,我们useReducer在这里添加一个,现在我们的代码看起来像这样:

import React, { createContext, useContext, useMemo, useReducer } from 'react'

const StoreContext = createContext()

export const StoreProvider = ({ children, initialState, reducer }) => {
  const [store, dispatch] = useReducer(reducer, initialState)

  const contextValue = useMemo(() => [store, dispatch], [store])

  return (
    <StoreContext.Provider value={contextValue}>
      {children}
    </StoreContext.Provider>
  )
}

export const useStore = () => {
  return useContext(StoreContext)
}

export default StoreContext
Enter fullscreen mode Exit fullscreen mode

上下文的问题在于,每当它发生更改时,其下的整个树都会重新渲染,这可能会带来巨大的性能问题。所以,即使我们只是调度一个 action,我们的组件也会重新渲染。现在,为了解决这个问题,让我们创建一个不同的上下文来存储调度函数,并将其与useDispatchhook 一起使用。

import React, { createContext, useContext, useReducer } from 'react'

const StoreContext = createContext()
export const DispatchContext = createContext()

export const StoreProvider = ({ initialState, reducer, children }) => {
  const [store, dispatch] = useReducer(reducer, initialState)

  return (
    <DispatchContext.Provider value={dispatch}>
      <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
    </DispatchContext.Provider>
  )
}

export const useStore = () => {
  return useContext(StoreContext)
}

export const useDispatch = () => {
  return useContext(DispatchContext)
}

export default StoreContext
Enter fullscreen mode Exit fullscreen mode

我们的使用方法是将App第一个包裹在我们的组件中DispatchContextStoreContext然后将第二个包裹在我们的组件中

import React, { useRef } from 'react'

import { useDispatch, useStore } from '@state/context-reducer'

const Example = () => {
  const dispatch = useDispatch()
  const store = useStore()

  return (
        <div className="my-3">
            <p>{JSON.stringify(store)}</p>
            <button onClick={() => dispatch({ type: 'increment' })}>
                Dispatch
            </button>
    </div>
  )
}

export default Example
Enter fullscreen mode Exit fullscreen mode

2.3 更进一步

那么,只有一个全局状态?你可能会疑惑。

卷起袖子,这就是生成器函数发挥作用的地方。基本上,我们可以创建一个函数makeStore,它接受 reducer 和 initialState,并为我们提供一个提供程序、一个 useStore 和一个 useDispatch,这样我们就可以轻松地创建多个存储。

import React, { createContext, useContext, useReducer } from 'react'

export default function makeStore(reducer, initialState) {
  const StoreContext = createContext(null)
  const DispatchContext = createContext(null)

  const StoreProvider = ({ children }) => {
    const [store, dispatch] = useReducer(reducer, initialState)

    return (
      <DispatchContext.Provider value={dispatch}>
        <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
      </DispatchContext.Provider>
    )
  }

  const useStore = () => {
    return useContext(StoreContext)
  }

  const useDispatch = () => {
    return useContext(DispatchContext)
  }

  return [StoreProvider, useStore, useDispatch]
}
Enter fullscreen mode Exit fullscreen mode

现在我们可以建立任意数量的商店!

const [LayoutStore, useLayout, useLayoutDispatch] = makeStore(layoutReducer, { menuOpen: false })
const [TodoStore, useTodo, useTodoDispatch] = makeStore(todosReducer, [])
Enter fullscreen mode Exit fullscreen mode

2.4 锦上添花

你可能会问,那么持久性又如何呢?

那怎么样?我说,只需在我们的函数中添加几行代码makeStore

export default function makeStore(reducer, initialState, key) {
  const StoreContext = createContext(null)
  const DispatchContext = createContext(null)

    let finalInitialState = null
    try {
        finalInitialState = JSON.parse(localStorage.getItem(key)) || initialState
    } catch(e) {}

    const finalReducer = (state, action) => {
        const newState = reducer(state, action)
        localStorage.saveItem(key, JSON.stringify(newState))
        return newState
    }

    // And now we use finalInitialState and finalReducer
    // instead of reducer and initialState
}
Enter fullscreen mode Exit fullscreen mode

这将使我们在开设所有商店时保持坚持。

等等,这难道不是全部在客户端吗?是的。所以在下一部分中,让我们看看如何将应用连接到服务器状态并使其正常运行。

文章来源:https://dev.to/patheticgeek/react-state-management-on-crack-55m8
PREV
为什么依赖关系很重要?立即将您的 Nuxt SSR(通用)应用部署到 Vercel。
NEXT
2020年职业发展规划——六个月回顾