React: Make it easier to debug when Context uses defaultValue accidentally due to no provider

Created on 29 Dec 2018  Â·  6Comments  Â·  Source: facebook/react

I just spend several hours debugging app blaming everything except me ofc.
I am using this useTheme Hook.

import React from 'react';
import ThemeContext from '../contexts/ThemeContext';

const useTheme = () => {
  const theme = React.useContext(ThemeContext);
  //if (theme == null)
  //  throw Error('useTheme: Please provide ThemeContext value.');
  return theme;
};

export default useTheme;

Some styles were light while other dark. Very strange.
Then I found the bug in my code, ThemeContext.Provider was sometimes used after using useTheme.
ThemeContext had an initial value different than provided.

While it's probably fine that React allows us to use default context value without a parent provider, it can lead to hard to find bugs.

Therefore, I decided to never provide default context value and throw an exception in useFooContext hook to warn about it.

Because of DX, React should reconsider default / initial context values. In my humble opinion.

Stale Feature Request

Most helpful comment

I think what you're saying is "React should make it easier to trace where the context value is coming from". Like a stack or something. That would clue you in earlier.

All 6 comments

I think what you're saying is "React should make it easier to trace where the context value is coming from". Like a stack or something. That would clue you in earlier.

@steida I've just had the inverse — I wanted to run unit tests without providing a context.

Here's an idea — use a destructuring assignment. It no provider is in sight, useContext returns undefined which cannot be destructured:

const {theme, toggle_theme} = useContext(ThemeContext);

P.S.
perhaps react gods can comment if the return value can be relied upon.
I'd rather see it changed to make testing easier :)

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution.

A guy on my team wrote a simple little thing to help with this:

import React, { useContext } from "react";

export const createRequiredContext = <TContext extends any>(name: string) => {
  const Context = React.createContext<TContext>(undefined as any);
  const providerName = `${name}Provider`;
  const hookName = `use${name}`;

  Context.displayName = providerName;

  const hook = () => {
    // This is a factory function for a custom hook; it will eventually be
    // run within a function component.
    // eslint-disable-next-line react-hooks/rules-of-hooks
    const context = useContext(Context);
    if (!context) {
      throw new Error(
        `${name} context not found. Please import ${providerName} and add it to a component above ${hookName}().`,
      );
    }
    return context;
  };
  return [Context.Provider, hook] as const;
};

It returns the provider and a hook and throws an error if there's no provider. It's saved us a bunch of times.

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

Was this page helpful?
0 / 5 - 0 ratings