Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to use useMediaQuery in class component

When using functions as components you have the ability to use the useMediaQuery hook from material-ui. However it no where shows you how to use this hook inside a class.

So I did some research and found out you can use it in a class by doing this:

import React from 'react';
import useMediaQuery from '@material-ui/core/useMediaQuery';

const withMediaQuery = (...args) => Component => props => {
  const mediaQuery = useMediaQuery(...args);
  return <Component mediaQuery={mediaQuery} {...props} />;
};

export default withMediaQuery;

However when adding it to the class like this:

export default withStyles(styles)(withMediaQuery(Main));

It gives me this error:

index.js:1 Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.

I really need to use the media query because some variables are dependent on them. This is the render method of the class which I would like to use the media query for.

render() {

    const { classes, children } = this.props;

    const isDesktop = useMediaQuery(theme => theme.breakpoints.up('lg'), {
      defaultMatches: true,
    });

    const shouldOpenSidebar = isDesktop ? true : this.state.openSidebar;

    return (
      <div
        className={cc({
          [classes.root]: true,
          [classes.shiftContent]: isDesktop,
        })}>
        <Topbar
          onSidebarOpen={this.handleSidebarOpen}
        />
        <Sidebar
          onClose={this.handleSidebarClose}
          open={shouldOpenSidebar}
          variant={isDesktop ? 'persistent' : 'temporary'}
        />
        <main className={classes.content}>
          {children}
        </main>
      </div>
    );
  }

I've already tried wrapping the component, but then I wouldn't be able to use the variables

like image 282
Ezrab_ Avatar asked Jan 09 '20 21:01

Ezrab_


People also ask

Can we use makeStyles in class component?

In order to use makeStyles function in your application you will need to import it in the component in where you plan to access the css that results from your makeStyles function. This is assuming you have already installed Material Ui in your project. This is in order for react to recognize it as a styles file.

Can I use props in class component?

Function and Class Components This function is a valid React component because it accepts a single “props” (which stands for properties) object argument with data and returns a React element. We call such components “function components” because they are literally JavaScript functions.


2 Answers

You're not supplying the args needed for useMediaQuery, so Main is passed as the args, and a function that expects the component is returned. When React tried to render (call the function), the return value is another function, which is not value as a react child.

Call the function - withMediaQuery and pass it the media queries, and then pass Main to the returned function.

Example:

export default withStyles(styles)(withMediaQuery('(min-width:600px)')(Main));
like image 72
Ori Drori Avatar answered Oct 15 '22 17:10

Ori Drori


Instead of limiting yourself to only one media query a better withMediaQuery HOC could be

import React from 'react'
import useMediaQuery from '@material-ui/core/useMediaQuery'

export const withMediaQuery = (queries = []) => Component => props => {
  const mediaProps = {}
  queries.forEach(q => {
    mediaProps[q[0]] = useMediaQuery(q[1])
  })
  return <Component {...mediaProps} {...props} />
}

This would allow you to pass in multiple queries as an array of arrays. Each entry would be a prop name and then the query.

export default withStyles(styles)(withMediaQuery([
    ['isDesktop', theme => theme.breakpoints.up('lg'), {
      defaultMatches: true
    }]
  ]))

In your component you could then request the prop names directly in render

render() {

    const { classes, children, IsDesktop = false } = this.props;

    const shouldOpenSidebar = IsDesktop ? true : this.state.openSidebar;

    return (
      <div
        className={cc({
          [classes.root]: true,
          [classes.shiftContent]: isDesktop,
        })}>
        <Topbar
          onSidebarOpen={this.handleSidebarOpen}
        />
        <Sidebar
          onClose={this.handleSidebarClose}
          open={shouldOpenSidebar}
          variant={isDesktop ? 'persistent' : 'temporary'}
        />
        <main className={classes.content}>
          {children}
        </main>
      </div>
    );
  }
like image 41
Frank Avatar answered Oct 15 '22 19:10

Frank