【问题标题】:React & TypeScript: Avoid context default valueReact 和 TypeScript:避免使用上下文默认值
【发布时间】:2020-08-03 14:07:47
【问题描述】:

为了更好地学习 React、TypeScript 和 Context / Hooks,我正在制作一个简单的 Todo 应用程序。但是,使上下文所需的代码感觉很麻烦。

例如,如果我想更改 Todo 的内容,我必须在三个地方更改它(ITodo 接口、默认上下文值、默认状态值)。如果我想传递新的东西,我必须在三个地方(TodoContext、TodoContext 的默认值和 value=)。有没有更好的办法不用写那么多代码?

import React from 'react'

export interface ITodo {
    title: string,
    body?: string,
    id: number,
    completed: boolean
}

interface TodoContext {
    todos: ITodo[],
    setTodos: React.Dispatch<React.SetStateAction<ITodo[]>>
}

export const TodoContext = React.createContext<TodoContext>({
    todos: [{title: 'loading', body: 'loading', id: 0, completed: false}],
    setTodos: () => {}
})

export const TodoContextProvider: React.FC<{}> = (props) => {
    const [todos, setTodos] = React.useState<ITodo[]>([{title: 'loading', body: 'loading', id: 0, completed: false}])

    return (
        <TodoContext.Provider value={{todos, setTodos}}>
            {props.children}
        </TodoContext.Provider>
    )
}

【问题讨论】:

  • 理想情况下这看起来不错,但您可以使用 redux 并将其存储在 redux-store 中,并使用 react-redux connect react-redux.js.org/api/connect 使其可在任何级别访问

标签: reactjs typescript react-hooks


【解决方案1】:

我的情况可能与您的情况有些不同(我意识到已经有一个公认的答案),但这似乎对我现在有用。 从上面 Aron 的回答中修改,因为在我的情况下使用该技术实际上不起作用。

我的实际上下文的名称当然不同。

export const TodoContext = createContext&lt;any&gt;({} as any)

【讨论】:

  • 我的回答对你不起作用怎么办?
【解决方案2】:

一段时间后,我想我找到了解决此问题的最佳方法。

import React from 'react'

export interface ITodo {
    title: string,
    body?: string,
    id: number,
    completed: boolean
}

const useValue = () => {
    const [todos, setTodos] = React.useState<ITodo[]>([])

    return {
        todos,
        setTodos
    }
}

export const TodoContext = React.createContext({} as ReturnType<typeof useValue>)

export const TodoContextProvider: React.FC<{}> = (props) => {
    return (
        <TodoContext.Provider value={useValue()}>
            {props.children}
        </TodoContext.Provider>
    )
}

这样,在您的上下文中添加新内容时会出现单点更改,而不是最初的三点更改。享受吧!

【讨论】:

    【解决方案3】:

    来自react documentation的备注:

    只有当组件在树中没有匹配的 Provider 时才使用 defaultValue 参数。

    我更喜欢这样做的方式是实际指定默认值可以是undefined

    const TodoContext = React.createContext<ITodoContext | undefined>(undefined)
    

    然后,为了使用上下文,我创建了一个钩子来为我做检查:

    function useTodoContext() {
      const context = useContext(TodoContext)
      if (context === undefined) {
        throw new Error("useTodoContext must be within TodoProvider")
      }
    
      return context
    }
    

    为什么我喜欢这种方法? 它会立即给我反馈为什么我的上下文值是undefined

    如需进一步参考,请查看Kent C. Dodds 的这篇博文

    【讨论】:

      【解决方案4】:

      没有办法避免声明接口和运行时值,因为 TS 的类型在运行时会消失,所以您只剩下运行时值。你不能从另一个生成一个。

      但是,如果您知道您只会访问 TodoContextProvider 组件中的上下文,您可以通过欺骗一点点来避免初始化 TodoContext,并告诉 TS 您传递的内容很好。

      const TodoContext = React.createContext<TodoContext>({} as TodoContext)
      

      如果您始终确保只访问TodoContextProvider 内部的上下文,其中todossetTodos 是用useState 创建的,那么您可以安全地跳过createContext 内部的初始化TodoContext,因为该初始value 永远不会被实际访问。

      【讨论】:

      • 那太好了。是的,通常情况就是这样。谢谢
      猜你喜欢
      • 1970-01-01
      • 2022-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多