Skip to content

Commit e17666e

Browse files
committed
Add typescript version of useMedia hook
1 parent 083c8d9 commit e17666e

File tree

1 file changed

+1
-0
lines changed

1 file changed

+1
-0
lines changed

src/pages/useMedia.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ links:
1212
name: Masonry Grid
1313
description: Original source of our useMedia v1 code. This demo uses react-spring to animate when images change columns.
1414
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}"
15+
tsCode: "import { useState, useEffect } from 'react';\r\n\r\nfunction App() {\r\n const columnCount = useMedia<number>(\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(() => []) as Array<DataProps[]>;\r\n\r\n (data as DataProps[]).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\nconst useMedia = <T>(queries: string[], values: T[], defaultValue: T) => {\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 values?.[index] || defaultValue;\r\n };\r\n\r\n // State and setter for matched value\r\n const [value, setValue] = useState<T>(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}"
1516
---
1617

1718
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)