Created
March 9, 2019 23:16
-
-
Save ryanflorence/818286f9cb0fefc131a2adb7e765d36c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useEffect } from "react"; | |
import ReactDOM from "react-dom"; | |
import makeHookCompat from "./src"; | |
// make a hook as usual | |
function useMedia(query) { | |
let [matches, setMatches] = useState(window.matchMedia(query).matches); | |
useEffect(() => { | |
let media = window.matchMedia(query); | |
let listener = () => setMatches(media.matches); | |
media.addListener(listener); | |
listener(); | |
return () => media.removeListener(listener); | |
}, [query]); | |
return matches; | |
} | |
// Here's the normal usage of the hook | |
function HookApp() { | |
let small = useMedia("(max-width: 400px)"); | |
let large = useMedia("(min-width: 800px)"); | |
return ( | |
<div className="Media"> | |
<h1>Media</h1> | |
<p>Small? {small ? "Yep" : "Nope"}.</p> | |
<p>Large? {large ? "Yep" : "Nope"}.</p> | |
</div> | |
); | |
} | |
// Since class components can't use hooks, we can wrap the hook in the class | |
// patterns for state composition: It returns a render prop component and a | |
// HOC. It's an array so you can name them | |
let [Media, withMedia] = makeHookCompat( | |
// pass in the hook | |
useMedia, | |
// and then map component props to hook arguments | |
// so <Media query="..."> will become useQuery(query) | |
props => [props.query] | |
); | |
// And now we can use the hook in a class! | |
//////////////////////////////////////////////////////////////////////////////// | |
// Render Props | |
class RenderPropApp extends React.Component { | |
render() { | |
return ( | |
<Media query="(max-width: 400px)"> | |
{small => ( | |
<Media query="(min-width: 800px)"> | |
{large => ( | |
<div className="Media"> | |
<h1>Media</h1> | |
<p>Small? {small ? "Yep" : "Nope"}.</p> | |
<p>Large? {large ? "Yep" : "Nope"}.</p> | |
</div> | |
)} | |
</Media> | |
)} | |
</Media> | |
); | |
} | |
} | |
ReactDOM.render(<RenderPropApp />, root); | |
//////////////////////////////////////////////////////////////////////////////// | |
// HOCs | |
class HOCAppImpl extends React.Component { | |
render() { | |
return ( | |
<div className="Media"> | |
<h1>Media</h1> | |
<p>Large? {this.props.large ? "Yep" : "Nope"}.</p> | |
</div> | |
); | |
} | |
} | |
const HOCApp = withMedia(matches => { | |
// map hook return value to HOC props | |
return { large: matches }; | |
})(HOCAppImpl); | |
ReactDOM.render(<HOCApp query="(min-width: 800px)" />, root); |
@mDibyo this package is a hack (it swaps the render
method of the provided class component), I wouldn't recommend it.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey @ryanflorence, I built something recently that let's you use hooks in your class components' render method. That API feels a lot cleaner to me, but maybe it doesn't account for all situations. Would love your feedback!
https://github.com/mDibyo/with-hooks-support