Skip to content

Commit

Permalink
feat: add support for meaningful default value when no arguments prov…
Browse files Browse the repository at this point in the history
…ided for the hook
  • Loading branch information
Viktor Vincze committed Nov 7, 2019
1 parent f1b1163 commit 38352f5
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 1 deletion.
89 changes: 89 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ setup({

# Usage

## With passing values

```js
useBreakpoint(defaultValue, breakpointValues)

Expand All @@ -57,6 +59,88 @@ useBreakpoint(defaultValue, breakpointValues)
is enough instead of `[['mobile', 300]]`
```

## Without passing values

In case you dont specify any value to the hook, it'll return a generated
object including boolean values for each breakpoint keys that's being
defined in options.

It'll return the following object with the basic setup.

```
{
isLandscape: false,
isPortrait: true,
isHDPI: false,
isMicro: false,
isMobile: true,
isTablet: false,
isSmall: false,
isMedium: false,
isLarge: false,
'is-Micro': false,
'is|Micro': false,
'isMicro+': true,
'is-Micro+': true,
'is|Micro+': true,
'isMicro-': false,
'is-Micro-': false,
'is|Micro-': false,
'is-Mobile': true,
'is|Mobile': true,
'isMobile+': true,
'is-Mobile+': true,
'is|Mobile+': true,
'isMobile-': true,
'is-Mobile-': true,
'is|Mobile-': true,
'is-Tablet': false,
'is|Tablet': false,
'isTablet+': false,
'is-Tablet+': false,
'is|Tablet+': false,
'isTablet-': true,
'is-Tablet-': true,
'is|Tablet-': true,
'is-Small': false,
'is|Small': false,
'isSmall+': false,
'is-Small+': false,
'is|Small+': false,
'isSmall-': true,
'is-Small-': true,
'is|Small-': true,
'is-Medium': false,
'is|Medium': false,
'isMedium+': false,
'is-Medium+': false,
'is|Medium+': false,
'isMedium-': true,
'is-Medium-': true,
'is|Medium-': true,
'is-Large': false,
'is|Large': false,
'isLarge+': false,
'is-Large+': false,
'is|Large+': false,
'isLarge-': true,
'is-Large-': true,
'is|Large-': true
}
```

Usage
```js
const { isMobile } = useBreakpoint()

// The above is basically a replacement for
const isMobile = useBreakpoint(false, ['mobile', true])
```

> You can also access the values with suffix and prefix but you need
> to rename the variables because it contains invalid character:
> `const { 'isMobile+': isMobile } = useBreakpoint()`
Component example

```jsx
Expand Down Expand Up @@ -106,6 +190,11 @@ You can also control your value to behave as `and up` and `and down`.

# FAQ

## Is there any best practice suggestion?

Yes! Use as less hooks as possible. It's always faster to have a single
`isMobile` variable and have simple conditions based on it.

## Which rule is being prioritized

The hook uses _eager_ evaluation, so the first truthy breakpoint value
Expand Down
5 changes: 5 additions & 0 deletions src/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ describe('useBreakpoint', () => {
it(`should work with single array value`, () => {
expect(calculateValue(300, ['mobile+', 500], 400)).toBe(500)
})

it(`should return 'is{Key}' properties if no props were given`, () => {
console.log(calculateValue(undefined, [], 500))
expect(calculateValue(undefined, [], 500).isMobile).toBe(true)
})
})
25 changes: 24 additions & 1 deletion src/useBreakpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,31 @@ type TCalculateValue = (
iw?: number
) => typeof defaultValue

// We will save the calculated value until innerWidth changes
let cachedProplessValue = {}
const calculateProplessValue = function(iw) {
if (cachedProplessValue[iw]) return cachedProplessValue[iw]

const isLandscape = getIsLandscape()
const proplessValue = { isLandscape, isPortrait: !isLandscape, isHDPI: window.devicePixelRatio > 1 }

for (const [[firstLetter, secondLetter, ...restLetter], [from, to]] of Object.entries(options.breakpoints)) {
const key = [LANDSCAPE, PORTRAIT].includes(firstLetter)
? `${firstLetter}${secondLetter.toUpperCase()}${restLetter.join('')}`
: `${firstLetter.toUpperCase()}${secondLetter}${restLetter.join('')}`
proplessValue[`is${key}`] = (iw > from && iw <= to)
}

cachedProplessValue = { [iw]: proplessValue }

return proplessValue
}

/* eslint-disable no-continue */
export const calculateValue: TCalculateValue = function(defaultValue, breakpointValues = [], iw = window.innerWidth) {
if (defaultValue === undefined && !breakpointValues.length) {
return calculateProplessValue(iw)
}
const isLandscape = getIsLandscape()
if (!breakpointValues || !breakpointValues.length) {
return defaultValue
Expand All @@ -46,7 +69,7 @@ export const calculateValue: TCalculateValue = function(defaultValue, breakpoint
export const useBreakpoint = function(defaultValue, breakpointValues) {
const [innerWidth, setInnerWidth] = useState(window.innerWidth)
useResize(() => setInnerWidth(window.innerWidth))
return useMemo(() => calculateValue(defaultValue, breakpointValues), [innerWidth, defaultValue])
return useMemo(() => calculateValue(defaultValue, breakpointValues, innerWidth), [innerWidth, defaultValue])
}

interface IOptions {
Expand Down

0 comments on commit 38352f5

Please sign in to comment.