【发布时间】:2020-12-08 11:58:17
【问题描述】:
我在为 React 上下文开发的生成器函数中理解记录类型时遇到了一些问题。
这是我的生成器函数:
import * as React from 'react'
export type State = Record<string, unknown>
export type Action = { type: string; payload?: unknown }
// eslint-disable-next-line @typescript-eslint/ban-types
export type Actions = Record<string, Function>
export type AppContext = { state: State; actions: Actions }
export type Reducer = (state: State, action: Action) => State
type ProviderProps = Record<string, unknown>
type FullContext = {
Context: React.Context<AppContext>
Provider: React.FC<ProviderProps>
}
/**
* Automates context creation
*
* @param {Reducer} reducer
* @param {Actions} actions
* @param {State} initialState
* @returns {Contex, Provider}
*/
export default (
reducer: Reducer,
actions: Actions,
initialState: State,
init: () => State = (): State => initialState,
): FullContext => {
const Context = React.createContext<AppContext>({
state: { ...initialState },
actions,
})
const Provider = ({ children }: { children?: React.ReactNode }) => {
const [state, dispatch] = React.useReducer(reducer, initialState, init)
const boundActions: Actions = {}
for (const key in actions) {
boundActions[key] = actions[key](dispatch)
}
return (
<Context.Provider value={{ state, actions: { ...boundActions } }}>
{children}
</Context.Provider>
)
}
return {
Context,
Provider,
}
}
问题的核心在这里:
const boundActions: Actions = {}
for (const key in actions) {
boundActions[key] = actions[key](dispatch)
}
我正在尝试做的是拥有一个可以使用 reducer 和初始状态调用的函数,以生成可以在应用程序中的任何位置使用的上下文提供程序。在这种情况下,我试图生成一个AuthContext。此上下文将采用如下所示的 reducer:
const authReducer: Reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'login':
return { ...state, isLogged: true, avoidLogin: false }
case 'logout':
return { ...state, isLogged: false, avoidLogin: true }
default:
return { ...state }
}
}
相当准系统,但它基本上会改变对象中的一些布尔值。
现在,这段代码确实有效,但让我烦恼的是在 Actions 类型的声明中使用了 Function 类型。
export type Actions = Record<string, Function>
使用unknown 也可以,但现在打字稿抱怨Object is of type 'unknown'. 在执行以下操作时:boundActions[key] = actions[key](dispatch),特别是在actions[key],因为我将它作为函数调用。
动作是接受一个函数(调度)作为参数并返回一个State对象的函数。
这是我的行动声明,以进一步说明:
const actions: Actions = {
login: (dispatch: (action: Action) => void) => () => {
dispatch({ type: 'login' })
},
logout: (dispatch: (action: Action) => void) => () => {
dispatch({ type: 'logout' })
},
}
如果我在生成器中控制台日志boundActions,我会得到类似:
Object {
"login": [Function anonymous],
"logout": [Function anonymous],
}
这正是我想要的,因为我希望能够在我的代码中的任何位置调用这些函数,而这又会调用特定 reducer 的调度函数来更改特定上下文的状态。
现在,我对 Typescript 还很陌生,但我的直觉告诉我 Actions 类型的声明应该是这样的:
export type Actions = Record<string, (dispatch:Function) => State>
这个问题是:
1- 它不起作用,因为现在boundActions 说:
Type 'Record<string, unknown>' is not assignable to type '(dispatch: Function) => Record<string, unknown>'.
Type 'Record<string, unknown>' provides no match for the signature '(dispatch: Function): Record<string, unknown>'.
2- 我仍在使用非类型安全的 Function 类型。
现在,dispatch 将一个动作作为参数export type Action = { type: string; payload?: unknown }
所以我的猜测是export type Actions = Record<string, (dispatch(arg:Action)) => State>
但这也不起作用,因为它不是有效的打字稿代码。
我知道这种情况有点复杂,但我只是想更好地了解 Record 在这种特定情况下的使用,其中记录具有 string 键和 Function 值。
在一天结束的时候,我只想做这样的事情:
export default function Login(): JSX.Element {
const {
actions: { login },
} = React.useContext(AuthContext)
return (
<View>
<Button text='Entrar' onPress={() => login()} />
</View>
)
}
这确实有效,但它不是类型安全的。
谢谢。
【问题讨论】:
标签: javascript reactjs typescript react-context