How do I share context between a library component, and my application component?
Asked Answered
A

1

12

I'm using lerna to create a monorepo where I'd have a structure like this:

root 
  packages
    application - Our root application 
    components  - Just some react components, that are to be used by the application

Here is a working Github with a simple example for this.

The issue I've run into, is I'm using Material-UI and its theming functionality, where at the application root we'll have a ThemeProvider:

import { ThemeProvider } from '@material-ui/styles';
//...

function App() {
  return (

  <ThemeProvider theme={theme}>
          <MyMaterialComponent/>
  </ThemeProvider>
  );
}

And later in the library component, we consume the theme, in our case using the makeStyles hook.

import React from 'react';
import { makeStyles } from '@material-ui/styles';
import Card from "@material-ui/core/Card";

const useStyles = makeStyles(theme => {
    console.log(theme); //When this component doesn't have access to the theme, this is `{}`
    return {
        root: {
            //color: theme.palette.primary.main  //will error
        }
    }
});

export function MyMaterialComponent({ }) {
    const classes = useStyles();
    return (<Card>
            <span className={classes.root}>This is some component</span>
        </Card>
    );

}

Now this seems all pretty straight forward. When we run this code all within the same package, it works fine. That styles function has access to the theme.

But when I'm running from another package (our application package), the the component library no longer has access to the theme (the theme is just an empty object).

The only way I currently know how to solve this, is the same way I've solved a similar hooks issue, which is to setup a webpack alias configuration in my application, to direct the component library to share the same same node module. (See this Github thread and the suggested solution).

ie. using react-app-rewired and customize-cra I have a config-overrides.js that looks like this:

const {
    override,
    addWebpackAlias,
  } = require("customize-cra");

const path = require('path'); 

module.exports = override( 
    addWebpackAlias({
        react: path.resolve('./node_modules/react'), 
        //comment out the line below to reproduce the issue
        "@material-ui/styles": path.resolve("./node_modules/@material-ui/styles") 
    })
)

or you could be managing your webpack manually to do a similar thing.

So this works fine, but this isn't a particularly satisfying solution.

Especially for a library like Material-UI, you want users to be able to use your component library without having them to mess with their webpack configuration.

So I figure I must be doing something wrong here - can you tell me what?

Aparri answered 28/5, 2019 at 8:42 Comment(3)
Ok, it's possible that this is more of an issue only when using the library via the lerna symlinks, and that if the library is published to npm, then it'll be fine. Can anyone confirm?Aparri
Did you manage to figure this one out? I'm hitting the same wall as you currently.Dowd
@Dowd - I'm using the solution I've outlined here, but yeah, it's not ideal and I've got to look at a better solution. The way I'm going to go about it, is to look at what other open source libraries are doing.Aparri
E
3

You can achieve that by placing your @material-ui/core dependency from devDependencies to peerDependencies in your library's project. In my case, that solved my problem.

More information on peerDependencies :

https://classic.yarnpkg.com/en/docs/dependency-types/#toc-peerdependencies

Equal answered 10/3, 2020 at 9:45 Comment(1)
This did not work for me. I m not using material Ui but using ANT but the use case is sameMess

© 2022 - 2024 — McMap. All rights reserved.