简体   繁体   中英

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

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));

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>
    );
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM