最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - how to use useMediaQuery in class component - Stack Overflow

programmeradmin5浏览0评论

When using functions as ponents 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 ponent, but then I wouldn't be able to use the variables

When using functions as ponents 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 ponent, but then I wouldn't be able to use the variables

Share Improve this question asked Jan 9, 2020 at 21:15 Ezrab_Ezrab_ 9837 gold badges21 silver badges49 bronze badges 6
  • Is there something special about your ponent that makes it difficult to convert from a class to a function ponent? – Ryan Cogswell Commented Jan 9, 2020 at 21:33
  • @RyanCogswell I just prefer to keep my code consistent since all my other ponents are also classes. – Ezrab_ Commented Jan 9, 2020 at 21:34
  • 1 You will find that more and more library APIs will be exposed only as hooks. It seems like you're just creating more work for yourself by trying to keep consistent in that fashion rather than gradually moving towards function ponents. – Ryan Cogswell Commented Jan 9, 2020 at 21:37
  • 1 Hmm I'm not sure about that since I believe that classes will be the future of js. So I'd rather start using them so that I could easily add more plex things to classes such as abstraction and stuff. Thanks for your advice though! – Ezrab_ Commented Jan 9, 2020 at 21:41
  • 1 Though they will continue to be supported, classes definitely are not the future of React ponents: reactjs/docs/…. – Ryan Cogswell Commented Jan 9, 2020 at 21:53
 |  Show 1 more ment

2 Answers 2

Reset to default 8

You're not supplying the args needed for useMediaQuery, so Main is passed as the args, and a function that expects the ponent 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 ponent 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>
    );
  }
发布评论

评论列表(0)

  1. 暂无评论