i am pretty new to react, react-hooks and js/ts.
At the moment i am coding a simple button which gets a state through useContext and updates that state with useReducer and a dispatch function.
I tried to seperate all of my code in specific files. The dispatch function from the reducer is passed as a value in the provider.
When i call the passed function in the consumer component i can give it a value of any type (number in my example) to the dispatcher. I set all types in the context, reducer, etc. VSCode is telling me the types which it expects - seems all correct. It still allows any type to get through. Why is that? I tried to console.log at all points where the value is passed, and it seems to override the type with type number, ignoring my specified types.
//app-state.tsx:
export type AppState = {
readonly FoldoutPosition: number | null,
readonly FoldoutType: FoldoutType | null
}
enum FoldoutType {
Projects,
Connection
}
//app-context.tsx
import { createContext, Dispatch } from 'react'
import { AppState} from '../states/app-state'
type ContextState =
{ AppState: AppState } &
{ changeFoldoutPosition: Dispatch<AppState["FoldoutPosition"]> } &
{ changeFoldoutType: Dispatch<AppState["FoldoutType"]> }
export default createContext<ContextState>({
AppState: {
FoldoutType: null,
FoldoutPosition: 0
},
changeFoldoutPosition: () => { },
changeFoldoutType: () => { }
});
//reducer.tsx
import { AppState } from '../states/app-state'
type Action =
| { readonly type: 'CHANGE_FOLDOUTPOSITION', readonly payload: AppState["FoldoutPosition"] }
| { readonly type: 'CHANGE_FOLDOUTTYPE', readonly payload: AppState["FoldoutType"] };
export const reducer = (state: AppState, action: Action): AppState => {
switch (action.type) {
case 'CHANGE_FOLDOUTPOSITION':
return { ...state, FoldoutPosition: action.payload };
case 'CHANGE_FOLDOUTTYPE':
return { ...state, FoldoutType: action.payload };
default:
return state
}
}
//wrapper.tsx
import React, { useReducer } from 'react'
import { reducer } from '../reducers/reducer'
import AppContext from './app-context'
import {AppState} from '../states/app-state'
const AppWrapper = (props: any) => {
const initalState = {
FoldoutType: null,
FoldoutPosition: 15
}
const [state, dispatch] = useReducer(reducer, initalState);
const changeFoldoutPosition = (FoldoutPosition: AppState["FoldoutPosition"]) => {
dispatch({type: 'CHANGE_FOLDOUTPOSITION', payload: FoldoutPosition});
}
const changeFoldoutType = (FoldoutType: AppState["FoldoutType"]) => {
dispatch({type: 'CHANGE_FOLDOUTTYPE', payload: FoldoutType});
}
return (
<AppContext.Provider
value={{
AppState: state,
changeFoldoutPosition: changeFoldoutPosition,
changeFoldoutType: changeFoldoutType
}}
>
{props.children}
</AppContext.Provider>
);
}
export default AppWrapper;
/// component:
import React, { useContext } from 'react'
import AppContext from '../contexts/app-context'
const TestComponent = (props: any) => {
const context = useContext(AppContext);
return (
//{context.AppState.Foldout}
<div>
<button onClick={() => context.changeFoldoutPosition(10)}>
1 - {context.AppState.FoldoutPosition} - {context.AppState.FoldoutType}
</button>
// The following button onClick should not work
// Type should be restricted to <FoldoutType | null>
<button onClick={() => context.changeFoldoutType(23)}>
2 - {context.AppState.FoldoutPosition} - {context.AppState.FoldoutType}
</button>
</div>
);
}
export default TestComponent