reactjsreact-hookspreactuse-contextcreatecontext

Conditionally execute useContext in React


I am looking to conditionally call useContext. This is my original code, which runs correctly, but fails tslint.

Component that calls useResourceScope - say Component1

import { useEffect } from 'react';

export function useSubscribeListItemStore(
  inputResources?: ResourceScope
) {
  const resources = inputResources || useResourceScope();
  const listItemStore = resources.consume(listItemStoreKey);
  
  useEffect(function subscribeListItemStore() {
    // do some logic
  }, []);
}

Component that defines useResourceScope - say Component2

import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

export const ResourceScopeContext = createContext<ResourceScope | undefined>(undefined);

export function useResourceScope(): ResourceScope {
  const resources = useContext(ResourceScopeContext);
  if (!resources) {
    throw new Error(
      'error message'
    );
  }
  return resources;
}

This gives me a tslint error: (react-hooks-nesting) A hook cannot be used in a conditional expression

This is my attempt at solving this, but does not work. It does not run correctly and fails at the useContext line in Component2 My thought was that since useContext must be called to fix the tslint errors, I decided to conditionally call the useContext with different created contexts. If the variable inputResources is passed in, it calls useContext with a placeholder context called PlaceHolderContext, which it ignores and returns inputResources. If not, it does useContext using the intended ResourceScopeContext value.

Component that calls useResourceScope - say Component1

import { useEffect } from 'react';

export function useSubscribeListItemStore(
  inputResources?: ResourceScope
) {
  const resources = useResourceScope(inputResources);
  const listItemStore = resources.consume(listItemStoreKey);
  
  useEffect(function subscribeListItemStore() {
    // do some logic
  }, []);
}

Component that defines useResourceScope - say Component2

import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

export const ResourceScopeContext = createContext<ResourceScope | undefined>(undefined);

export function useResourceScope(inputResources?: ResourceScope): ResourceScope {
  const PlaceHolderContext = createContext({ foo: 'bar' });
  const createdContext = inputResources ? PlaceHolderContext : ResourceScopeContext;

  const resources = useContext(createdContext as any);

  if (inputResources) {
    return inputResources;
  }

  if (!resources) {
    throw new Error(
      'error message'
    );
  }
  return resources as any;
}

Could you suggest how I can resolve tslint error here without breaking it?


Solution

  • Rather than conditionally calling the hook, can you conditionally use its return value?

    import { useEffect } from 'react';
    
    export function useSubscribeListItemStore(
      inputResources?: ResourceScope
    ) {
      const contextResources = useResourceScope();
      const resources = inputResources || contextResources;
    
      const listItemStore = resources.consume(listItemStoreKey);
      
      useEffect(function subscribeListItemStore() {
        // do some logic
      }, []);
    }