Harnessing the power of Material-UI's muiThemeable with Typescript

I'm trying to access the current theme colors within a custom React component. The example I'm referencing is from Material UI http://www.material-ui.com/#/customization/themes

Despite attempting various approaches, I'm unable to get Typescript to compile my component successfully. The error message I encounter is: error TS2345: Argument of type 'typeof MainHeader' is not assignable to parameter of type 'Component<{}, {}>'.

import * as React from "react";
import muiThemeable from 'material-ui/styles/muiThemeable';

interface Properties  {
  title: string
}

class MainHeader extends React.Component<Properties, any> {

render() {
  return (
    <div className='hero' >
      <div className='logo' />
      <div className="bar" />

      <h1>{this.props.title}</h1>
    </div>
  );
  }
}

export default muiThemeable()(MainHeader);

Answer №1

it looks like the material-ui definition for the muiThemeable function may be incorrect...

when you write:

export default muiThemeable()(MainHeader);

it is essentially the same as writing:

export const themed = muiThemeable<MainHeader, {}, {}>()(MainHeader)

this won't compile because the component property type and state were omitted

the compiler infers:

<TComponent<P, S>,{}, {}>

which does not align with the Function constraints

        export function muiThemeable<TComponent extends (React.Component<P, S>), P, S>()
            : (component: TComponent ) => TComponent;

...to satisfy the compiler, we need to add the missing constraints by doing:

export default (props: Properties) => muiThemeable<MainHeader, Properties, any>()(new MainHeader(props));

however, this provides an instance when a class is expected

if you proceed with...

// ...    
import Themeable from "./themeable";
let  props = { title: "hello!" };
ReactDOM.render(<Themeable {...props}/>, document.getElementById("main"));

it will not work

but if you modify the muiThemeable definition like so:

 export function muiThemeable<TComponent extends (React.Component<P, S>), P, S>()
        : (component: Function ) => TComponent;

then you can use:

export default muiThemeable<MainHeader, Properties, any>()( MainHeader);

although tsc will generate errors:

JSX element type 'Themeable' does not have any construct or call signatures

it will transpile correctly and function

however, this is not ideal because:

  • it doesn't build
  • Function is NOT describing the right parameter type
  • TComponent does not describe what is returned

it seems like it returns an instance instead of a class or type

ultimately, considering the following signature may make more sense:

export function muiThemeable<TComponent extends (React.Component<P, S>), P, S>(): (component: new()=> TComponent ) => ( new() =>  TComponent);

after reviewing the source code at source,

export function muiThemeable<TComponent extends React.Component<P, S>, P extends MuiThemeProviderProps, S> (Component: new () => TComponent): React.StatelessComponent<P> 

this could provide an alternative approach to redefine or enhance the definition.
By wrapping the function
...and potentially utilizing a decorator for efficiency and readability...

    import * as React from "react";
    import * as ReactDOM from "react-dom";
    import * as injectTapEventPlugin from "react-tap-event-plugin";

    // Needed for onTouchTap
    // http://stackoverflow.com/a/34015469/988941
    injectTapEventPlugin();


    import darkBaseTheme from "material-ui/styles/baseThemes/darkBaseTheme";
    import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
    import getMuiTheme from "material-ui/styles/getMuiTheme";
    import muiThemeable from "material-ui/styles/muiThemeable";

    import AppBar from "material-ui/AppBar";
    import MuiThemeProviderProps = __MaterialUI.Styles.MuiThemeProviderProps;

    function Themeable<TComponent extends React.Component<P, any>, P extends MuiThemeProviderProps> (Component: new () => TComponent): new() => TComponent {
        return muiThemeable<TComponent, P, any>()(Component as any) as any;
    }

    const themeable = <P, TFunction extends React.ComponentClass<P>>(target: TFunction): TFunction => {
        return Themeable(target as any) as any;
    };

    export interface MyBarProps extends __MaterialUI.AppBarProps, __MaterialUI.Styles.MuiThemeProviderProps {
        // ...
    }

    @themeable
    export class MyBar extends React.Component<MyBarProps, any> {

        constructor(props?: MyBarProps, context?: any) {
            super(props, context);
        }

        render() {
            if (!this.props.muiTheme) {
                throw new Error("muiTheme not Found");
            }
            return (
                <AppBar {...this.props} />
            );
        }
    }

    const darkTheme = getMuiTheme(darkBaseTheme);
    darkTheme.appBar.color = "red";

    const Main = () => (
        <MuiThemeProvider muiTheme={darkTheme}>
            <MyBar title="My AppBar" />
        </MuiThemeProvider>
    );

    ReactDOM.render(<Main></Main>,document.getElementById("root"));

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Typescript filtering function that results in an empty array

Struggling with filtering an array in typescript and aurelia as I keep getting empty lists. For example, when searching for the keyword ra within the firstName property, I expect to retrieve the object with the name "Raja". Not sure where I'm going w ...

Every time I try to install create-react-app, I keep encountering a frustrating 'network Socket timeout' error

$ npx create-react-app amazon-clone npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead. Creating a new React app in D:\js\faceboom. npm WARN config global `--global`, `--local` are deprecated. ...

What is the best way to create a mock URL in axios for unit testing purposes?

Scenario In this application, the URL is only accessible in production and cannot be accessed locally. During unit testing, it becomes necessary to mock the response from that URL. Solution Refer to this helpful tutorial for guidance. Current Implement ...

The "keydown" event in React will not alter the state

I am currently developing an application that requires me to track the keys pressed by the user. I am utilizing keydown and keyup events for this purpose. However, I am facing a challenge where I do not want the same key to be registered multiple times whe ...

I'm struggling to find the right Typescript syntax for defining a thunk function that returns a value while using React Redux Toolkit

Currently, I am utilizing TypeScript within a React Redux Toolkit project. While attempting to create an Async Thunk action function that is expected to return a boolean value, I found myself struggling with determining the correct TypeScript syntax: expor ...

Custom cellRenderer prevents Ag Grid's autoHeight and wrapText features from functioning properly

I've been attempting to adjust the formatting of a long cell value by wrapping the text. According to the documentation, setting autoHeight=true and wrapText=true works fine without any cellRenderer components. However, when using a cellRendererFramew ...

How can you verify the data type of an object without resorting to type guarding

I have a situation where I need to deal with different types of objects. export interface A { links: number name: string } export interface B { cat: boolean name: string } Initially, I considered using this method: const IsTypeB = (obj: any): obj ...

What is the process for incorporating a responsive range into a mui class?

Looking for a way to apply margin within the range of 565-350px in MUI4 using only one media query line. Any suggestions on how to achieve this? view: { display: 'flex', justifyContent: 'center', flexWrap: &apos ...

Setting state back to default following the conditional rendering of a React component

Whenever the save button is clicked, I aim to display a snackbar component by updating the showSnackbar state to true. To achieve this in React, it's just a simple conditional check in the main render method. The snackbar I'm using here automatic ...

The Material-UI Button Component is experiencing issues after being deployed on Github Pages and is not functioning as expected

<Button href={node.slug}> <span>Read more</span> </Button> Essentially, the code above represents a button inside a Card component. If further clarification is needed, please don't hesitate to ask. The variable node.slug res ...

Relocating the node_modules folder results in syntax errors arising

I encountered a perplexing syntax error issue. I noticed that having a node_modules directory in the same location I run npm run tsc resolves the issue with no syntax errors. However, after relocating the node_modules directory to my home directory, ~ , a ...

Guide to configuring browserify

After installing the modules (browserify, react, reactify), I attempted to process a JSX file using browserify. var React = require("react"); var App = React.createClass({ render: function () { return <h1>111</h1> } }); React ...

Retrieve: Type 'string | undefined' does not match the parameter type 'RequestInfo'

When using the fetch function, I encountered an error with the "fetchUrl" argument: Error: Argument of type 'string | undefined' is not assignable to parameter of type 'RequestInfo'. This is the code snippet where the error occurred: ...

encountering an issue with server-side rendering of React causing an error

Node.js has been a bit of a challenge for me, especially when it comes to working with react and express. I have been struggling to find comprehensive tutorials and troubleshooting resources, leading me to ask minimal questions in the correct manner. While ...

Spacing between table cells is only applied to the first row and footer

In an attempt to add spacing between the first row, second row, and footer of a table, I have experimented with cell spacing combined with border-collapse. However, the spacing is being applied all over the table instead of in specific areas. I am utilizin ...

The makeStyles feature is currently not functioning properly in the latest version of Next.js with Material UI v5

Currently, I am utilizing nextjs along with material ui in my project "@mui/material": "^5.0.1", "@mui/styles": "^5.0.1", Below is the code snippet of my component structure import { AppBar, Toolbar, Typography, Box ...

Importing Heroicons dynamically in Next.js for more flexibility

In my Next.js project, I decided to use heroicons but faced a challenge with dynamic imports. The current version does not support passing the icon name directly to the component, so I created my own workaround. // HeroIcon.tsx import * as SolidIcons from ...

Error: Material-UI prop type validation failed. Please specify either children, image, src, or component prop for CardMedia component. This error occurred at

I encountered an issue while trying to utilize the CardMedia component from Material-ui. The error message received was 'Failed prop type: Material-UI: Either children, image, src, or component prop must be specified. at CardMedia'. I attempted v ...

Can you please provide the range of values for lodash/debounce?

Could someone clarify what the acceptable minimum and maximum values are for debounce in react js? Appreciate it! ...

combining the package.json files of the client and server into a single file

I need assistance with merging server-side code (in nodejs) and client-side code (with react) into a single package.json file. The server file is located in the project root directory, while the client-side code resides in the /client folder along with oth ...