Reputation: 5150
I have this route that has a login and logout button, the error in the browser is 'Property 'dispatch' does not exist on type '{}'.'
(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 };
I think I have the context wrong
(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 };
The reducer feels right
(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 };
And the route I believe is right
(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 };
Upvotes: 1
Views: 973
Reputation: 9672
Set as initial value for the Context the properties that will later be used. Otherwise any Component that uses the Context, on the first render, will get undefined properties.
So either set some kind of value for the property that the Provider will fill later, or check in each Component that uses the Context if the property exists (not recommended).
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: () => {}
});
Upvotes: 1