Skip to content

Commit fad212a

Browse files
committed
♻️ Use hooks in CurrentMediaProvider
1 parent 2206357 commit fad212a

File tree

1 file changed

+47
-52
lines changed

1 file changed

+47
-52
lines changed

src/current-media-provider/index.js

+47-52
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import PropTypes from 'prop-types'
2-
import React, { createContext, Component } from 'react'
2+
import React, { createContext, useLayoutEffect, useState } from 'react'
33
import { withTheme } from 'emotion-theming'
44
import { reduceObj } from '@exah/utils'
5+
import { withDisplayName } from '../utils'
56

67
const listenForChanges = (target, fn) => {
78
fn()
@@ -14,67 +15,61 @@ const INITIAL_STATE = {
1415
currentMediaKey: []
1516
}
1617

17-
const { Provider, Consumer } = createContext(INITIAL_STATE)
18+
const Context = createContext(INITIAL_STATE)
19+
const { Provider, Consumer } = Context
1820

19-
class CurrentMediaProvider extends Component {
20-
static defaultProps = {
21-
theme: {},
22-
media: null
21+
const updateCurrentMedia = (key, mql) => (currentMediaKey) => {
22+
if (mql.matches) {
23+
return [ ...currentMediaKey, key ]
2324
}
24-
static propTypes = {
25-
theme: PropTypes.object.isRequired,
26-
media: PropTypes.object
27-
}
28-
state = INITIAL_STATE
29-
listeners = []
30-
setCurrentMedia = (key, mediaQueryList) => {
31-
this.setState((prevState) => {
32-
const nextMediaKey = prevState.currentMediaKey.slice(0)
33-
34-
if (mediaQueryList.matches) {
35-
nextMediaKey.push(key)
36-
} else {
37-
const index = nextMediaKey.indexOf(key)
38-
if (index === -1) return
39-
nextMediaKey.splice(index, 1)
40-
}
41-
42-
return {
43-
currentMediaKey: nextMediaKey
44-
}
45-
})
46-
}
47-
componentDidMount () {
48-
const media = this.props.media || this.props.theme.media || {}
4925

50-
this.listeners = reduceObj((acc, key, query) => {
51-
const mediaQueryList = window.matchMedia(query)
52-
const listener = () => this.setCurrentMedia(key, mediaQueryList)
26+
return currentMediaKey.filter((item) => item !== key)
27+
}
5328

54-
return [ ...acc, listenForChanges(mediaQueryList, listener) ]
55-
}, [], media)
56-
}
57-
componentWillUnmount () {
58-
this.listeners.map((fn) => fn())
59-
}
60-
render () {
61-
return (
62-
<Provider value={this.state}>
63-
{this.props.children}
64-
</Provider>
65-
)
66-
}
29+
function CurrentMediaProvider ({
30+
theme = {},
31+
media = theme.media || {},
32+
children
33+
}) {
34+
const [ currentMediaKey, setCurrentMedia ] = useState(INITIAL_STATE.currentMediaKey)
35+
36+
useLayoutEffect(() => {
37+
const listeners = reduceObj((acc, key, query) => {
38+
const mql = window.matchMedia(query)
39+
const listener = () => setCurrentMedia(updateCurrentMedia(key, mql))
40+
41+
return [ ...acc, listenForChanges(mql, listener) ]
42+
}, [])(media || {})
43+
44+
return () => listeners.map((fn) => fn())
45+
}, [ media ])
46+
47+
return (
48+
<Provider value={{ currentMediaKey }}>
49+
{children}
50+
</Provider>
51+
)
52+
}
53+
54+
CurrentMediaProvider.propTypes = {
55+
theme: PropTypes.shape({ media: PropTypes.object }),
56+
media: PropTypes.object
6757
}
6858

6959
const CurrentMediaProviderWithTheme = withTheme(CurrentMediaProvider)
7060

71-
const withCurrentMedia = (BaseComponent) => (props) => (
72-
<Consumer>
73-
{(state) => <BaseComponent {...state} {...props} />}
74-
</Consumer>
75-
)
61+
const withCurrentMedia = (Comp) => {
62+
const HOC = (props) => (
63+
<Consumer>
64+
{(state) => <Comp {...state} {...props} />}
65+
</Consumer>
66+
)
67+
68+
return withDisplayName(`withCurrentMedia(${Comp.displayName || 'Component'})`)(HOC)
69+
}
7670

7771
export {
72+
Context as CurrentMediaContext,
7873
CurrentMediaProviderWithTheme as CurrentMediaProvider,
7974
Consumer as CurrentMediaConsumer,
8075
withCurrentMedia

0 commit comments

Comments
 (0)