16

I've built a ReactJS component library that I use for multiple projects installed via an NPM package using a sim link. I want to use the context API to pass data from a parent component served from the component library to my base project to be consumed by multiple consumer components also served from the component library. When I try the context is always undefined in my child components.

If I place my consumer component in my provider component within my library it works like a champ but this defeats what I'm trying to achieve. If I export both the provider and the consumer to my base project the consumer doesn't see the provider.

This is from my base project

import { Screen, COD, GenericSocketServer } from 'component-library'

export default class View extends React.PureComponent {
  render() {
    return (
      <Screen className="screen odmb1">
        <GenericSocketServer>
          <COD />
        </GenericSocketServer>
      </Screen>
    )
  }
}

This is my provider code exported from my 'component-library'

import React from 'react';
import MyContext from "./context";
import COD from './../cod';

export default class GenericSocketServer extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{ foo: 'bar' }}>
        <COD />
        {this.props.children}
      </MyContext.Provider>
    );
  }
}

This is my content code used in 'component-library'

import React from 'react'
const MyContext = React.createContext()
export default MyContext

This is my consumer component exported from 'component-library'

import MyContext from "../GenericSocketServer/context"

class COD extends React.Component {
  render() {
    return (
      <React.Fragment>
        <MyContext.Consumer>
          {(context) => { 
            /*
               context comes back undefined 
               I expect { foo: 'bar' }
            */
            console.log('context :', context)
            return (
              <p>This should work</p>
          )}}
        </MyContext.Consumer>
      </React.Fragment>
    )
  }
}

Context always comes back undefined as if it doesn't see the parent provider. I think I'm ether doing something wrong initializing the context myself or for some reason the two components I'm importing just don't share the same context. Please help!! Not sure if I should give up on this and just use redux.

Reedling78
  • 343
  • 1
  • 2
  • 10
  • Did you ever figure this out? I am having the same exact issue. I posted a similar (slightly more specific) question [here](https://stackoverflow.com/questions/59641585/react-leaflet-custom-component-context-not-being-passed). How to use context within an npm published react component? – Seth Lutske Jan 21 '20 at 18:19
  • Never did, I gave up and started passing props. Though, I would love a solution to this. This is how I want to use the context API if can get it to work. – Reedling78 Jan 21 '20 at 22:07
  • I ended up figuring out the answer to my problem in my other thread. I'm going to post an answer to my own question when I get a chance. I was building my component with webpack before publishing to npm. The problem had everything to do with the way I was configuring my webpack.config file and my package.json file. Are you building your npm package with webpack? If so, what do your config and package files look like? – Seth Lutske Feb 27 '20 at 21:36
  • @SethLutske would love to see your answer, as I'm having the same issue – syberen Mar 11 '20 at 11:35
  • I wrote up [an answer](https://stackoverflow.com/a/60639968/12010984) to my question. My problem is not identical to this one, but they seem similar. I'm not sure my answer will pinpoint the problem here, but perhaps it may be helpful. – Seth Lutske Mar 11 '20 at 16:04

3 Answers3

5

Maybe you are making multiple instances of the component providing the context. Let's say you have a component Sound, which starts by:

    const { Provider, Consumer } = React.createContext();

If you import this library from your main project, the context will be created at the global space. You then use it to render your document tree. But in another component you also imported this library, which had to be resolved during webpack transpilation. It thus has its own copy of the above lines and a context object created in its own space. The problem occurs when you try to use the Consumer, because the Provider was only made by the main project for the first context object, and the second context's provider instance was never instantiated, thus returns undefined.

A solution to the problem is to enforce a single context object, which you can achieve by telling the second component's webpack that the provider-owning library is an external, so when webpack reaches e.g. the "import sound" line, it will not go further and will assume this dependency is resolved at runtime. When runtime comes, it will take it from the same place where the main project is taking it. To do this in webpack, e.g. for above "sound" library, add this to your other component (not main project):

{
   ...
   externals: {
      ...
      'sound': 'sound'
   }
   ...
}

Also in your component package.json:

{
   ...
   peerDependencies: {
     "sound": "^1.2.3"
   }
}
Darko Maksimovic
  • 899
  • 10
  • 12
0

should your code of consumer be

      <React.Fragment>
        <MyContext.Consumer>
          {value => /* render something based on the context value */}
        </MyContext.Consumer>
      </React.Fragment>

as stated from the official react doc : https://zh-hant.reactjs.org/docs/context.html

when you define

enter image description here

you can use it like

enter image description here

DigitalFreak
  • 346
  • 1
  • 8
0

Apart from Darko's answer, esm and cjs export is also a possible reason for context to fail in a package. If you use the hook in esm and the provider in cjs, you will not get the value for that context.

John Winston
  • 493
  • 1
  • 10