为了在 Typescript 中更改来自消费者的上下文,将什么设置为提供者的值?

What to set as the Provider's value in order to change context from a Consumer, in Typescript?

我正在尝试学习如何在 React 中使用钩子和上下文,但需要使用 TypeScript(也是新手)。

我的用例是授权:用户登录或未登录,并具有角色(例如 'Admin'、'Super Admin')。根据他们的授权状态,导航栏呈现不同。

除其他外,我正在尝试遵循 this 示例,根据我的需要进行了简化。

到目前为止,这是我的上下文和提供者:

import React, { createContext, useContext, Context, FC SetStateAction, useState } from 'react';

export interface IAuth {
    loggedIn: boolean,
    role: string
}

export const AuthContext = React.createContext<IAuth>({
    loggedIn: false,
    role: ''
});

const AuthProvider: FC = ({children}) => {

    const [auth, setAuth] = useState({
        loggedIn: false,
        role: ''
    });

    return (
        <AuthContext.Provider value={/*What to set as value?*/}>
            {children}
        </AuthContext.Provider>
    )

}

export default AuthProvider;

然后是我的导航栏,它使用了上下文的消费者:

class Nav extends React.Component {

    render() {

        return (

            <AuthContext.Consumer>
                {context => (
                    < div >
                       .. stuff ...
                        </div>
                        <ul className="nav">
                            {context.loggedIn ? (
                                <Fragment> .. links .. </Fragment>
                            ) : (<Fragment> .. links .. </Fragment>
                            )}
                        </ul>
                    </div>

                )
                }
            </AuthContext.Consumer>

        )
    }
} export default Nav;

如果我这样设置 Provider 的值,所有这些都可以正常工作:

<AuthContext.Provider value={{
    loggedIn: false,
    role: 'someRole'
}}>

但是,我还想提供 setAuth 功能,以便从登录组件等更改这些值。

使用以下命令会出现错误:

<EmailContext.Provider value={[state, setState]}>

Type '({ loggedIn: boolean; role: string; } | Dispatch<SetStateAction<{ loggedIn: boolean; role: string; }>>)[]' is missing the following properties from type 'IAuth': loggedIn, role

使用以下也给我错误:

   <AuthContext.Provider value={{data: auth,
                                    updateAuth: () => { setAuth({... auth, loggedIn: true})}
        }}>

data: auth 错误:Type '{ data: { loggedIn: boolean; role: string; }; updateAuth: () => void; }' is not assignable to type 'IAuth'. Object literal may only specify known properties, and 'data' does not exist in type 'IAuth'.

我尝试过的各种其他事情也出现类似错误。

这些都是 Typescript 错误。如何以 Typescript 兼容的方式设置我的 Provider 值 - 并包括 set 操作而不仅仅是值?

要在您的上下文中使用 auth 和 setAuth 状态,这是一种方法:

export const AuthContext = React.createContext<IAuth>({{}, ()=> {}});
const [auth, setAuth] = useState({
        loggedIn: false,
        role: ''
    });

    return (
        <AuthContext.Provider value={{auth, setAuth}}>
            {children}
        </AuthContext.Provider>
    )

在nav组件中,可以导入{AuthContext}

const {auth, setAuth} = React.useContext(AuthContext)

您不需要使用带钩子的 renderProps。您可以在代码中直接使用 auth 的值。

PS:我没用过typescript,但是react的方式还是一样的。

首先定义一个接口,其中包含应通过上下文传递的所有内容

export interface IAuth {
    loggedIn: boolean;
    role: string;
    logIn: () => void;
    logOut: () => void;
}

然后是提供者

export const AuthContext = React.createContext<IAuth>(null);

const AuthProvider: FC = ({ children }) => {
    const [auth, setAuth] = useState({
        loggedIn: false,
        role: ""
    });

    const logIn = () => {
        setAuth(prevState => ({
            ...prevState,
            loggedIn: true
        }));
    };

    const logOut = () => {
        setAuth(prevState => ({
            ...prevState,
            loggedIn: false
        }));
    };

    return (
        <AuthContext.Provider
            // Here as value you need to pass the same interface as IAuth
            // You can also just pass setAuth and do whatever you want
            // from the children
            value={{ 
                ...auth,
                logIn,
                logOut
            }}>
            {children}
        </AuthContext.Provider>
    );
};

然后在您的消费者中,您可以使用 useContext 挂钩来访问值

const Child: FC = () => {
    const { loggedIn, role, logIn, logOut } = useContext(AuthContext);

    return (
        <div>
            <p>{`loggedIn: ${loggedIn}`}</p>
            <p>{`role: ${role}`}</p>
            <button onClick={logIn}>logIn</button>
            <button onClick={logOut}>logOut</button>
        </div>
    );
};

最后将 Child 渲染为 AuthProvider

的子级
const App: FC = () => (
    <AuthProvider>
        <Child />
    </AuthProvider>
);