Search code examples
javascriptreactjstypescriptreact-context

React context and nullable values in state


I'm trying to make a context in react that will hold some values as defined by an interface (Client in the example below). My problem is that it requires me to make that field nullable in the state interface (ClientState), meaning I would have to check for null values everywhere I consume the context. I want to avoid that.

Example code:

interface Client {
  value1: string,
  value2: number
}

interface ClientState {
    client?: Client
}

const initialState: ClientState = {
    client: undefined
}

const ClientContext = React.createContext<ClientState>(initialState);

export const useClient = (): ClientState => useContext(ClientContext);

export const EmployeeContextProvider = ({ children }: PropsWithChildren<{}>) => {
    const [state, setState] = useState({});

    // abstracted, not relevant to this problem
    const loadFiles = () => {
        setState(
            {
                value1: "test",
                value2: 1
            }
        )
    }

    useEffect(() => loadFiles(), []);

    return (
        <ClientContext.Provider value={state}>
                {children}
        </ClientContext.Provider>
    )
}

So far I've tried and deemed unsatisfactory:

  1. Giving the client field in initialState a dummy object. The problem with this is that the real version of this has a large number of these fields, meaning lots of dummy code.
  2. Adding a check to useClient() for members of the Client interface, same problem as 1.

Also of semi-relevance is that I don't need to modify this context beyond initialization and it's perfectly fine for it to be read-only.


Solution

  • client needs to be optional because its value is set from state which is initialised to an empty object. The type of state is inferred as ClientState.

    useState<ClientState>({}) would require all properties of ClientState to be optional.

    You could force TypeScript to accept an empty (dummy) object as if it complied with ClientState using useState({} as ClientState) but that means your Provider really will be providing an unusable client until setState has been invoked with a real one.

    But that seems to be the problem you would prefer, over checking for null/undefined each time you wish to make use of the client...

    TypeScript is perhaps saving you from yourself here. If your client really can be undefined then you should check it every time you use it!