React 和 TypeScript:避免使用上下文默认值

Posted

技术标签:

【中文标题】React 和 TypeScript:避免使用上下文默认值【英文标题】:React & TypeScript: Avoid context default value 【发布时间】: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 使其可在任何级别访问 【参考方案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 永远不会被实际访问。

【讨论】:

那太好了。是的,通常情况就是这样。谢谢

以上是关于React 和 TypeScript:避免使用上下文默认值的主要内容,如果未能解决你的问题,请参考以下文章

React useContext & Typescript:不是数组类型

webpack 5 下的 react-typescript

React 状态管理:上下文 API 作为全局存储

如何使用enum作为react / typescript中的道具

套接字 IO 客户端 + React Typescript + 上下文 API 导致 TypeError:无法分配给对象“#<Object>”的只读属性“exports”

如何在带有 Typescript 的 React 项目中组织类型定义