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
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.
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.
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>
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With