Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Typescript issue and reducer state doesn't add use on click. Property 'state and dispatch' does not exist on type 'UserContextType | null'

I am new to typescript with react and tried so many thing but doesn’t work. state doesn’t work and getting typescript error Property ‘state and dispatch’ does not exist on type ‘UserContextType | null’
UserContRed.tsx

export const UserContRed = () => {

   //const context = useContext(ContRedContext)
    const {state , dispatch } = useContext(ContRedContext)
   
    const [firstname, setFirstname] = useState('')
    const [id, setId ] = useState(0)
    const [lastname, setLastname] = useState('')
 
    // let displayUsers =  context?.state.users.map( (u:any) => {
    let displayUsers =  state.users.map( (u:any) => {
        return(
            <ul key={u.id}>
                <li>{u.id}</li>
                <li>{u.firstname}</li>
                <li>{u.lastname}</li>
            </ul>
        ) 
    }) 
    const addUser = () => {
        const addUser = () => dispatch({type:ActionType.ADD, payload : {id:1,firstname:firstname, lastname:lastname}})
    }

    return(
        <div>
          <div>
            <label>Id: </label>
            <input type="text" onChange={(e)=>{setId(Number(e.target.value))}} />
          </div>
          <div>
            <label>Firstname: </label>
            <input type="text" onChange={(e)=>{setFirstname(e.target.value)}} />
          </div>
          <div>
            <label>Lastname: </label>
            <input type="text" onChange={(e)=>{setLastname(e.target.value)}} />
          </div>
            <button onClick={addUser}>sendToStore</button>

          
        </div>
       
    )
}

ContRed.tsx


export type User = {
    id: number;
    firstname: string;
    lastname: string; 
}

type contredProps = {
    children: React.ReactNode
}


export type IuserState = {
    users : User[]
}
const initialState: IuserState = {
    users : [
        {
            id:0,
            firstname:'',
            lastname: ''
        }
    ]
}
export enum ActionType {
    ADD="add",
    REMOVE="remove"
}


type  userAction = {
    type: ActionType,
    payload: User
}


export const reducer = (state: IuserState, action: userAction) : typeof initialState  => {
    
    switch(action.type){
        case "add": 
                return ({...state, users: [...state.users,
                                                    {id:action.payload.id,
                                                     firstname:action.payload.firstname,
                                                     lastname:action.payload.lastname   
                                                    }
                                                ]
                        })
        case "remove": 
                        return ({...state, users: [
                                    ...state.users.filter(data => data.id !== action.payload.id)]
                                })
        default:
            return state;
    }
}


type UserContextType = {
    state: IuserState
    dispatch: React.Dispatch<userAction>
}

export const ContRedContext = createContext<UserContextType|null>({
    state:initialState,
    dispatch: () => {}
})



export const ContRedContextProvier = ({children}:contredProps) => {
    
    const[state,dispatch] = useReducer(reducer,initialState as IuserState);
    const value : UserContextType = { state, dispatch}
    
    return(
        <ContRedContext.Provider value={value}>
            {children}
        </ContRedContext.Provider>
    )

}

App.tsx

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

    <ContRedContextProvier>
        <UserContRed/>
    </ContRedContextProvier>
    

I tried with using constant instead de-structuring typescript error goes away but it doesn’t add or doesn’t update the state. typescript is so hard seems to me with react please explain or guide what i am doing wrong.
Below tried typescript is fine but functionality doesn’t work

  const context = useContext(ContRedContext)
  let displayUsers =  context?.state.users.map( (u:any) => { ...}
   const addUser = () => context?.state.dispatch({type:ActionType.ADD, payload : {id:1,firstname:firstname, lastname:lastname}})

Tried:

 const context = useContext(ContRedContext)
  let displayUsers =  context?.state.users.map( (u:any) => { ...}
   const addUser = () => context?.state.dispatch({type:ActionType.ADD, payload : {id:1,firstname:firstname, lastname:lastname}})

Expected:
User should add to state and render in child component.

>Solution :

You have typed the ContRedContext to be nullable, e.g. createContext<UserContextType | null>({ .... }) , so consumers would need to use a null-check prior to accessing state or dispatch.

Example:

const context = useContext(ContRedContext);

if (!context) return null;
  
const { state, dispatch } = context; // not null 🙂

But you don’t appear to ever pass a null context value, even for the default context value, and likely have no need to.

Update the ContRedContext context type declaration to remove the null type:

export const ContRedContext = createContext<UserContextType>({
  state: initialState,
  dispatch: () => {}
});

There is an additional issue in the UserContRed component with the addUser handler. It declares a function that issues the dispatch and never calls it.

Issue code:

const addUser = () => {
  const addUser = () => // <-- never called
    dispatch({
      type: ActionType.ADD,
      payload: { id, firstname: firstname, lastname: lastname }
    });
};

Update to just call dispatch:

const addUser = () => {
  dispatch({
    type: ActionType.ADD,
    payload: { id, firstname, lastname }
  });
};
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading