【问题标题】:React context and nullable values in state在状态中反应上下文和可为空的值
【发布时间】:2021-05-19 18:36:43
【问题描述】:

我正在尝试在 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 相同的问题。

还有一个半相关性,我不需要在初始化之外修改这个上下文,它是只读的就很好。

【问题讨论】:

  • 我只想指出,只有在 DOM 中没有可用的匹配上下文时,您才会真正看到 initialState 的值。换句话说,只要有合适的 Context 提供者,你实际上永远不会遇到这个值。当您调用useContext(ClientContext) 时,您将获得的默认值将是您在EmployeeContextProvider 中传递给useState 的值。提供给createContext 的初始值基本上仅用于调试,从不相关。您可能已经意识到这一点,只是想确定一下。
  • 我知道,但这并不能解决字段可以为空的问题,这意味着我每次使用上下文时都必须检查。
  • state 的初始值不为空,并且您从未将state 的值设置为空,因此它永远不会为空。这意味着,您不需要检查state 是否为空,即使它可以为空。我错过了什么吗?
  • @HenrikErstad 这个答案有帮助吗? stackoverflow.com/questions/61333188/…

标签: javascript reactjs typescript react-context


【解决方案1】:

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

useState&lt;ClientState&gt;({}) 要求ClientState 的所有属性都是可选的。

您可以强制 TypeScript 接受一个空的(虚拟)对象就好像它使用 useState({} as ClientState)用真实的调用。

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

TypeScript 可能会在这里拯救你。如果您的客户端确实可以未定义,那么您应该在每次使用时检查它!

【讨论】:

  • 当使用与所需类型不匹配的对象强制输入 TypeScript 时,as 仍然会由于类型没有充分重叠而产生错误。一个小小的谎言往往会导致一个更大的谎言......useState({} as unknown as ClientState)
  • 因为在效果运行一次之前,您的 Provider 不会有适合 state 的值,因此您可以有条件地渲染 Provider 及其子项。一旦设置了客户端,它将呈现。 return (state &amp;&amp; state.client ? (&lt;existing_render_stuff&gt;) : null)
  • 请参阅我的其他答案以获得更明确的解决方案,而无需任何说教;)
  • 非常感谢您非常好的回答。我同意它并不完美,但该字段需要为空的唯一原因是上下文需要一个默认值,并且在初始化上下文后读取填充状态字段的文件
【解决方案2】:

我认为这可以满足您的需求。

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

这允许空客户端处于状态,但不在上下文中。守卫确保在设置客户端之前不会渲染 Context 及其子项。

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
    )
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-31
    相关资源
    最近更新 更多