使用TypeScript,功能组件和挂钩在React上下文提供程序中设置状态

时间:2020-09-04 11:30:53

标签: reactjs typescript

我同时开始使用React和TypeScript,在我的应用程序中实现一些基本身份验证时遇到了一个问题。我一直以Ryan Chenkie的Orbit App及其关于React安全的课程为例。

现在,我陷入了编译器,抱怨SignIn.tsx中的TS2722错误(无法调用可能未定义的对象)。我的怀疑是,我要做的就是在所有数据结构和函数调用上设置适当的类型,但是如何以及在哪里设置它们,在某种程度上暗示了我。所以,这是代码:

App.tsx:这里没什么,只是一个包装在上下文提供程序中的应用程序。

import { AuthContext, authData } from "./AuthContext"

const defAuth:authData = {
  userId: 0,
  name: '',
  token: '',
  expiresAt: ''
}

const App = () => {
  return (
    <AuthContext.Provider value={{ authData:defAuth }}>
      <Main />    
    </AuthContext.Provider>
  );
}

AuthContext.tsx:调用我尝试使用各种默认参数的createContext()时,通常的想法是可以调用authContext.setState()并将数据传递给它。我使用的是Partial前缀,因此不必将setState()传递给Provider元素。

export interface authData {
  userId: number
  name: string
  token: string
  expiresAt: string
}

interface IAuthContext {
  authData: authData,
  setState: (authInfo:authData) => void
}

const AuthContext = createContext<Partial<IAuthContext>>(undefined!)
const { Provider } = AuthContext


const AuthProvider: React.FC<{}> = (props) => {
  const [authState, setAuthState] = useState<authData>()
  const setAuthInfo = (data:authData) => {
    console.log('Called setAuthInfo')
    setAuthState({
      userId: data!.userId,
      name: data!.name,
      token: data!.token,
      expiresAt: data!.expiresAt
    })
  }

  return (
    <Provider
      value={{
        authData: authState,
        setState: (authInfo:authData) => setAuthInfo(authInfo)
      }} {...props}
    />
  )
}

export { AuthContext, AuthProvider }

SignIn.tsx:这又是一个基本的登录组件,带有表单和onSubmit处理函数。一切正常,直到我向其添加authContext为止。我只包含了相关代码。

interface loginType extends Record<string, any>{
  email: string,
  password: string,
  remember: boolean
}

const SignIn = () => {
  const authContext = useContext(AuthContext)
  const { register, handleSubmit, errors } = useForm<loginType>()

  const onSubmit = async (data:loginType) => {
    const ret = await apiFetch.post('process_login/', formData )
    console.log(ret.data)
    console.log('Printing context')
    authContext.setState(ret.data)
    console.log(authContext)
  }
   /* ... ... */
}

如前所述,编译器在SignIn.tsx的{​​{1}}抱怨说它可能是未定义的。我尝试使用各种参数调用authContext.setState(ret.data),试图传递一些默认值,这些默认值将告诉编译器稍后在运行时定义createContext()的位置。我尝试以几种不同的方式调用setState,但没有任何效果。

这显然可以在普通JSX中使用,我真的很想找到一种使其在TSX中可以使用的方法。

1 个答案:

答案 0 :(得分:0)

这是您需要做的:

首先在App.tsx中,您必须使用AuthProvider而不是AuthContext.Provider。这样,您就可以摆脱value属性。

<AuthProvider>
  <Main />
</AuthProvider>

然后,在AuthContext.tsx中,创建上下文时无需使用Partial前缀。因此,对IAuthContext进行一些调整,然后在创建上下文时传递一些默认数据。

interface IAuthContext {
  authData: authData | undefined,
  setState: (authInfo:authData) => void
}


const AuthContext = createContext<IAuthContext>( {
  authData: defaultAuthData,
  setState: () => {}
})

现在您可以调用authContext.setState(),将数据作为authData类型进行传递。