在状态中反应上下文和可为空的值

React context and nullable values in state

我正在尝试在 React 中创建一个上下文,它将保存一些由接口定义的值(下例中的 Client)。我的问题是它要求我在状态接口 (ClientState) 中使该字段可为空,这意味着我必须在使用上下文的任何地方检查空值。我想避免这种情况。

示例代码:

interface Client {
  value1: string,
  value2: number
}

interface ClientState {
    client?: Client
}

const initialState: ClientState = {
    client: undefined
}

const ClientContext = React.createContext<ClientState>(initialState);

export const useClient = (): ClientState => useContext(ClientContext);

export const EmployeeContextProvider = ({ children }: PropsWithChildren<{}>) => {
    const [state, setState] = useState({});

    // abstracted, not relevant to this problem
    const loadFiles = () => {
        setState(
            {
                value1: "test",
                value2: 1
            }
        )
    }

    useEffect(() => loadFiles(), []);

    return (
        <ClientContext.Provider value={state}>
                {children}
        </ClientContext.Provider>
    )
}

到目前为止,我已经尝试并认为不满意:

  1. initialState 中的 client 字段提供一个虚拟对象。这个的问题是这个的真实版本有大量的这些字段,意味着有很多伪代码。
  2. Client 接口的成员添加对 useClient() 的检查,与 1.
  3. 相同的问题

另外一个半相关的是我不需要在初始化之外修改这个上下文,它是只读的完全没问题。

client 需要是可选的,因为它的值是从初始化为空对象的 state 设置的。 state 的类型被推断为 ClientState.

useState<ClientState>({}) 要求 ClientState 的所有属性都是可选的。

您可以强制 TypeScript 接受一个空(虚拟)对象 ,就好像它 使用 useState({} as ClientState) 遵守 ClientState 一样,但这意味着您的 Provider 确实会提供一个不可用的对象客户端直到 setState 被真正的客户端调用。

但这似乎是你更喜欢的问题,而不是每次你想使用客户端时检查 null/undefined...

TypeScript 或许可以让您远离自我。如果您的客户端确实可以未定义,那么您应该每次使用它时检查它!

我想这就是你想要的。

您的 ClientState 类型似乎只用于允许未定义客户端,您说您不想要,所以我假设您不想拥有它。此外,它与您设置 Client 而不是 ClientState 的 setState 调用冲突。

这允许状态为空的客户端,但不在上下文中。守卫确保在设置客户端之前不会呈现上下文及其子项。

import React, { PropsWithChildren, useContext, useEffect, useState } from "react";

interface Client {
    value1: string,
    value2: number
}

// Note: ClientContext is initialised with an unusable Client object
const ClientContext = React.createContext<Client>({} as Client);

export const useClient = (): Client => useContext(ClientContext);

export const EmployeeContextProvider = ({ children }: PropsWithChildren<{}>) => {
    // We allow state to be set to null
    const [state, setState] = useState<Client | null>(null);

    // abstracted, not relevant to this problem
    const loadFiles = () => {
        setState(
            {
                value1: "test",
                value2: 1
            }
        )
    }

    useEffect(() => loadFiles(), []);

    // Guard against null so that state can be provided as a Client
    return (
        state != null ?
            <ClientContext.Provider value={state} >
                {children}
            </ClientContext.Provider>
            : null
    )
}