Skip to content

Commit 2f9ef33

Browse files
committed
Updated useMedia: now supports any media query (not just screen width)
1 parent eb90387 commit 2f9ef33

File tree

1 file changed

+5
-2
lines changed

1 file changed

+5
-2
lines changed

src/pages/useMedia.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ date: "2019-01-24"
55
gist: https://gist.github.com/gragland/ed8cac563f5df71d78f4a1fefa8c5633
66
sandbox: https://codesandbox.io/s/6jlmpjq9vw
77
links:
8+
- url: https://gist.github.com/gragland/ed8cac563f5df71d78f4a1fefa8c5633
9+
name: useMedia v1
10+
description: Original version of this recipe that uses a single event listener on browser resize. Works well, but only for screen width media queries.
811
- url: https://codesandbox.io/s/26mjowzpr?from-embed
912
name: Masonry Grid
10-
description: Original source of our useMedia hook code. This demo uses react-spring to animate when images change columns.
11-
code: "import { useState, useEffect } from 'react';\r\n\r\nfunction App() {\r\n const columnCount = useMedia(\r\n \/\/ Media queries\r\n ['(min-width: 1500px)', '(min-width: 1000px)', '(min-width: 600px)'],\r\n \/\/ Column counts (relates to above media queries by array index)\r\n [5, 4, 3],\r\n \/\/ Default column count\r\n 2\r\n );\r\n\r\n \/\/ Create array of column heights (start at 0)\r\n let columnHeights = new Array(columnCount).fill(0);\r\n\r\n \/\/ Create array of arrays that will hold each column's items\r\n let columns = new Array(columnCount).fill().map(() => []);\r\n\r\n data.forEach(item => {\r\n \/\/ Get index of shortest column\r\n const shortColumnIndex = columnHeights.indexOf(Math.min(...columnHeights));\r\n \/\/ Add item\r\n columns[shortColumnIndex].push(item);\r\n \/\/ Update height\r\n columnHeights[shortColumnIndex] += item.height;\r\n });\r\n\r\n \/\/ Render columns and items\r\n return (\r\n <div className=\"App\">\r\n <div className=\"columns is-mobile\">\r\n {columns.map(column => (\r\n <div className=\"column\">\r\n {column.map(item => (\r\n <div\r\n className=\"image-container\"\r\n style={{\r\n \/\/ Size image container to aspect ratio of image\r\n paddingTop: (item.height \/ item.width) * 100 + '%'\r\n }}\r\n >\r\n <img src={item.image} alt=\"\" \/>\r\n <\/div>\r\n ))}\r\n <\/div>\r\n ))}\r\n <\/div>\r\n <\/div>\r\n );\r\n}\r\n\r\n\/\/ Hook\r\nfunction useMedia(queries, values, defaultValue) {\r\n \/\/ State update function\r\n const match = () => {\r\n \/\/ Get first media query that matches\r\n const query = queries.findIndex(q => matchMedia(q).matches);\r\n \/\/ Return related value or defaultValue if none\r\n return values[query] || defaultValue;\r\n };\r\n \r\n \/\/ State and setter for current value\r\n const [value, set] = useState(match);\r\n\r\n useEffect(() => {\r\n \/\/ Update state on window resize\r\n \/\/ Usage of match function defined outside of useEffect ...\r\n \/\/ ... ensures that it has current values of arguments.\r\n const handler = () => set(match);\r\n window.addEventListener('resize', handler);\r\n \/\/ Remove event listener on cleanup\r\n return () => window.removeEventListener('resize', handler);\r\n }, []); \/\/ Empty array ensures effect is only run on mount and unmount\r\n\r\n return value;\r\n}\r\n"
13+
description: Original source of our useMedia v1 code. This demo uses react-spring to animate when images change columns.
14+
code: "import { useState, useEffect } from 'react';\r\n\r\nfunction App() {\r\n const columnCount = useMedia(\r\n \/\/ Media queries\r\n ['(min-width: 1500px)', '(min-width: 1000px)', '(min-width: 600px)'],\r\n \/\/ Column counts (relates to above media queries by array index)\r\n [5, 4, 3],\r\n \/\/ Default column count\r\n 2\r\n );\r\n\r\n \/\/ Create array of column heights (start at 0)\r\n let columnHeights = new Array(columnCount).fill(0);\r\n\r\n \/\/ Create array of arrays that will hold each column's items\r\n let columns = new Array(columnCount).fill().map(() => []);\r\n\r\n data.forEach(item => {\r\n \/\/ Get index of shortest column\r\n const shortColumnIndex = columnHeights.indexOf(Math.min(...columnHeights));\r\n \/\/ Add item\r\n columns[shortColumnIndex].push(item);\r\n \/\/ Update height\r\n columnHeights[shortColumnIndex] += item.height;\r\n });\r\n\r\n \/\/ Render columns and items\r\n return (\r\n <div className=\"App\">\r\n <div className=\"columns is-mobile\">\r\n {columns.map(column => (\r\n <div className=\"column\">\r\n {column.map(item => (\r\n <div\r\n className=\"image-container\"\r\n style={{\r\n \/\/ Size image container to aspect ratio of image\r\n paddingTop: (item.height \/ item.width) * 100 + '%'\r\n }}\r\n >\r\n <img src={item.image} alt=\"\" \/>\r\n <\/div>\r\n ))}\r\n <\/div>\r\n ))}\r\n <\/div>\r\n <\/div>\r\n );\r\n}\r\n\r\n\/\/ Hook\r\nfunction useMedia(queries, values, defaultValue) {\r\n \/\/ Array containing a media query list for each query\r\n const mediaQueryLists = queries.map(q => window.matchMedia(q));\r\n\r\n \/\/ Function that gets value based on matching media query\r\n const getValue = () => {\r\n \/\/ Get index of first media query that matches\r\n const index = mediaQueryLists.findIndex(mql => mql.matches);\r\n \/\/ Return related value or defaultValue if none\r\n return typeof values[index] !== 'undefined' ? values[index] : defaultValue;\r\n };\r\n\r\n \/\/ State and setter for matched value\r\n const [value, setValue] = useState(getValue);\r\n\r\n useEffect(\r\n () => {\r\n \/\/ Event listener callback\r\n \/\/ Note: By defining getValue outside of useEffect we ensure that it has ...\r\n \/\/ ... current values of hook args (as this hook callback is created once on mount).\r\n const handler = () => setValue(getValue);\r\n \/\/ Set a listener for each media query with above handler as callback.\r\n mediaQueryLists.forEach(mql => mql.addListener(handler));\r\n \/\/ Remove listeners on cleanup\r\n return () => mediaQueryLists.forEach(mql => mql.removeListener(handler));\r\n },\r\n [] \/\/ Empty array ensures effect is only run on mount and unmount\r\n );\r\n\r\n return value;\r\n}"
1215
---
1316

1417
This hook makes it super easy to utilize media queries in your component logic. In our example below we render a different number of columns depending on which media query matches the current screen width, and then distribute images amongst the columns in a way that limits column height difference (we don't want one column way longer than the rest).

0 commit comments

Comments
 (0)