React:TypeScript:使用useReducer设置和获取全局上下文

时间:2019-11-23 11:51:18

标签: reactjs typescript

我有一条具有登录和注销按钮的路由,浏览器中的错误是类型'{}'上不存在'属性'dispatch'。'

(home.tsx)

    import React, { useContext, useEffect, useRef, useState } from 'react';
    import { Dispatch, Global } from '../components/context';
    import { LOG_IN, LOG_OUT} from '../components/reducer';

    const Home: React.FC = () => {
      const { global } = useContext(Global);
      const { dispatch } = useContext(Dispatch);

      const login = () => {
        dispatch({ type: LOG_IN});
      };

      const logout = () => {
        dispatch({ type: LOG_OUT});
      };
      return (
      <div>
        {global.loggedIn && <div>You are logged in</div>}
        {!global.loggedIn && <div>You are logged out</div>}
        <br/>
        <button onClick={login}>Log In</button>
        <button onClick={logout}>Log Out</button>
      </div>
      );
    };

    export { Home };


我认为上下文有误

(context.tsx)

    import { createContext } from 'react';
    import { InitialState } from './reducer';

    const Dispatch = createContext({}); // <--- I'm missing somthing here??

    const Global = createContext({
      global: InitialState,
    });

    export { Dispatch, Global };


减速器感觉正确

(reducer.tsx)

    const LOG_IN = 'LOG_IN';
    const LOG_OUT = 'LOG_OUT';

    interface StateInterface {
      loggedIn: boolean;
    }
    const InitialState: StateInterface = {
      loggedIn: true,
    };

    interface ActionInterface {
      type: string;
    }
    const Reducer = (state: StateInterface, action: ActionInterface) => {
      switch (action.type) {
        case 'LOG_IN':
          return {
            ...state,
            loggedIn: true,
          };
        case 'LOG_OUT':
          return {
            ...state,
            loggedIn: false,
          };
        default:
          return state;
      }
    };
    export { Reducer, InitialState, LOG_IN, LOG_OUT };


我认为正确的路线

(Router.tsx)

    import React, { useReducer } from 'react';
    import { BrowserRouter, Route, Switch } from 'react-router-dom';
    import { Dispatch, Global } from './components/context';
    import { Layout } from './components/layout';
    import { InitialState, Reducer } from './components/reducer';
    import { Home } from './routes/home';
    import { NotFound } from './routes/notFound';

    const Router: React.FC = () => {
      const [global, dispatch] = useReducer(Reducer, InitialState);
      return (
        <Dispatch.Provider value={{ dispatch }}>
          <Global.Provider value={{ global }}>
            <BrowserRouter>
              <Route
                render={({ location }) => (
                  <Layout location={location}>
                    <Switch location={location}>
                      <Route exact path='/' component={Home} />
                      <Route component={NotFound} />
                    </Switch>
                  </Layout>
                )}
              />
            </BrowserRouter>
          </Global.Provider>
        </Dispatch.Provider>
      );
    };

    export { Router };

1 个答案:

答案 0 :(得分:1)

将稍后将使用的属性设置为Context的初始值。否则,在第一次渲染时使用Context的任何Component都将获得未定义的属性。

因此,请为提供者稍后填充的属性设置某种值,或者如果属性存在,则签入使用Context的每个组件(不建议)。

import { Dispatch as ReactDispatch } from "react";

// We assign the same type the property is going to have later
const Dispatch = createContext<{ dispatch: ReactDispatch<ActionInterface> }>({
    dispatch: () => {}
});