|
| 1 | +## API Reference (array destructuring) |
| 2 | + |
| 3 | +### How to import? |
| 4 | + |
| 5 | +``` |
| 6 | +import { useBoolean } from 'react-hanger/array' // will import all of functions |
| 7 | +import useBoolean from 'react-hanger/array/useBoolean' // will import only this function |
| 8 | +``` |
| 9 | + |
| 10 | +### useBoolean |
| 11 | + |
| 12 | +```jsx |
| 13 | +const [showCounter, actions] = useBoolean(true); |
| 14 | +``` |
| 15 | + |
| 16 | +Actions: |
| 17 | + |
| 18 | +- `toggle` |
| 19 | +- `setTrue` |
| 20 | +- `setFalse` |
| 21 | + |
| 22 | +### useNumber |
| 23 | + |
| 24 | +```jsx |
| 25 | +const [counter] = useNumber(0); |
| 26 | +const [limitedNumber] = useNumber(3, { upperLimit: 5, lowerLimit: 3 }); |
| 27 | +const [rotatingNumber] = useNumber(0, { |
| 28 | + upperLimit: 5, |
| 29 | + lowerLimit: 0, |
| 30 | + loop: true |
| 31 | +}); |
| 32 | +``` |
| 33 | + |
| 34 | +Actions: |
| 35 | + |
| 36 | +Both `increase` and `decrease` take an optional `amount` argument which is 1 by default, and will override the `step` property if it's used in the options. |
| 37 | + |
| 38 | +- `increase(amount = 1)` |
| 39 | +- `decrease(amount = 1 )` |
| 40 | + |
| 41 | +Options: |
| 42 | + |
| 43 | +- `lowerLimit` |
| 44 | +- `upperLimit` |
| 45 | +- `loop` |
| 46 | +- `step` - sets the increase/decrease amount of the number upfront, but it can still be overriden by `number.increase(3)` or `number.decrease(5)` |
| 47 | + |
| 48 | +### useInput |
| 49 | + |
| 50 | +This one is unique, since it returns tuple as a first element, where first element is `value` and second is `hasValue` |
| 51 | +Second element is `actions` as usual |
| 52 | + |
| 53 | +```typescript |
| 54 | +type UseInputActions = { |
| 55 | + setValue: React.Dispatch<SetStateAction<string>>; |
| 56 | + onChange: (e: React.SyntheticEvent) => void |
| 57 | + clear: () => void |
| 58 | +} |
| 59 | +type UseInput = [[string, boolean], UseInputActions] |
| 60 | +``` |
| 61 | +
|
| 62 | +```jsx |
| 63 | +const [[newTodo], actions] = useInput(""); |
| 64 | +``` |
| 65 | + |
| 66 | +```jsx |
| 67 | +<input value={newTodo} onChange={actions.onChange} /> |
| 68 | +``` |
| 69 | +Actions: |
| 70 | + |
| 71 | +- `clear` |
| 72 | +- `onChange` - default native event.target.value handler |
| 73 | + |
| 74 | +Properties: |
| 75 | + |
| 76 | +- `hasValue` - |
| 77 | + |
| 78 | +### useBindToInput |
| 79 | + |
| 80 | +Designed to be used in composition with `useInput`. |
| 81 | +First and second elements are the same as `useInput. |
| 82 | +Third are bindings to spread. |
| 83 | + |
| 84 | +```jsx |
| 85 | +const [[newTodo], actions, { nativeBind, valueBind }] = useBindToInput(useInput("")); |
| 86 | +``` |
| 87 | + |
| 88 | +```jsx |
| 89 | +<input value={newTodo} onChange={actions.onChange} /> |
| 90 | +<input {...nativeBind} /> |
| 91 | +<Slider {...valueBind} /> |
| 92 | +``` |
| 93 | + |
| 94 | +Actions: |
| 95 | + |
| 96 | +- `nativeBind` - binds the `value` and `onChange` props to an input that has `e.target.value` |
| 97 | +- `valueBind` - binds the `value` and `onChange` props to an input that's using only `value` in `onChange` (like most external components) |
| 98 | + |
| 99 | +### useArray |
| 100 | + |
| 101 | +```jsx |
| 102 | +const [todos, actions] = useArray([]); |
| 103 | +``` |
| 104 | + |
| 105 | +Actions: |
| 106 | + |
| 107 | +- `add` |
| 108 | +- `clear` |
| 109 | +- `removeIndex` |
| 110 | +- `removeById` - if array consists of objects with some specific `id` that you pass |
| 111 | +all of them will be removed |
| 112 | +- `move` - moves item from position to position shifting other elements. |
| 113 | +``` |
| 114 | + So if input is [1, 2, 3, 4, 5] |
| 115 | + |
| 116 | + from | to | expected |
| 117 | + 3 | 0 | [4, 1, 2, 3, 5] |
| 118 | + -1 | 0 | [5, 1, 2, 3, 4] |
| 119 | + 1 | -2 | [1, 3, 4, 2, 5] |
| 120 | + -3 | -4 | [1, 3, 2, 4, 5] |
| 121 | +``` |
| 122 | + |
| 123 | +### useMap |
| 124 | + |
| 125 | +```jsx |
| 126 | +const [someMap, someMapActions] = useMap([["key", "value"]]); |
| 127 | +const [anotherMap, anotherMapActions] = useMap(new Map([["key", "value"]])); |
| 128 | +``` |
| 129 | + |
| 130 | +Actions: |
| 131 | + |
| 132 | +- `set` |
| 133 | +- `remove` |
| 134 | +- `clear` |
| 135 | +- `initialize` - applies tuples or map instances |
| 136 | +- `setValue` |
| 137 | + |
| 138 | + |
| 139 | +## useSetState |
| 140 | + |
| 141 | +```jsx |
| 142 | +const [state, setState] = useSetState({ loading: false }); |
| 143 | +setState({ loading: true, data: [1, 2, 3] }); |
| 144 | +``` |
| 145 | + |
| 146 | +Actions: |
| 147 | + |
| 148 | +- `setState(value)` - will merge the `value` with the current `state` (like this.setState works in React) |
| 149 | + |
| 150 | +Properties: |
| 151 | + |
| 152 | +- `state` - the current state |
| 153 | + |
| 154 | +## usePrevious |
| 155 | + |
| 156 | +Use it to get the previous value of a prop or a state value. |
| 157 | +It's from the official [React Docs](https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state). |
| 158 | +It might come out of the box in the future. |
| 159 | + |
| 160 | +```jsx |
| 161 | +const Counter = () => { |
| 162 | + const [count, setCount] = useState(0); |
| 163 | + const prevCount = usePrevious(count); |
| 164 | + return ( |
| 165 | + <h1> |
| 166 | + Now: {count}, before: {prevCount} |
| 167 | + </h1> |
| 168 | + ); |
| 169 | +}; |
| 170 | +``` |
| 171 | + |
| 172 | +### Migration from object to array based |
| 173 | + |
| 174 | +All value based hooks like `useBoolean`, `useNumber` etc. Are changed to |
| 175 | +be using arrays, since it's more safe for reference equality, and also |
| 176 | +makes it easier to use many `useSmth` without renaming `value` in destructuring. |
| 177 | + |
| 178 | +So if you had |
| 179 | +```javascript |
| 180 | +const { value: showHeader, ...showHeaderActions } = useBoolean(true) |
| 181 | +const { value: showFooter, ...setShowFooterActions } = useBoolean(true) |
| 182 | +``` |
| 183 | +It will become |
| 184 | +```javascript |
| 185 | +const [showHeader, showHeaderActions] = useBoolean(true) |
| 186 | +const [showFooter, showFooterActions] = useBoolean(true) |
| 187 | +``` |
| 188 | + |
| 189 | +Note that despite this code seems to be looking the same, it's not. Cause `showHeaderActions` in v1 will result |
| 190 | +in new object reference every rerender (because spreading creates new object, hence new reference). While in v2 actions are memoized |
| 191 | +using `useMemo` and their reference will not change, cause they are not rely on value. |
| 192 | +It enables us passing `actions` down the props without useless re-renders and excessive destructuring, it prevents `useEffects` and |
| 193 | +other hooks from re-run/new reference creation if autofix of ESLint rule `react-hooks/extraneous-deps` will add them as dependencies automatically. |
| 194 | + |
| 195 | +### useInput migration |
| 196 | +Also big change to the `useInput` |
| 197 | +If before you was not using `eventBind` and `nativeBind` from them, then using the same approach from above |
| 198 | +you will get what you want. |
| 199 | +But if you need bindings you need to compose `useInput` with `useBindToInput` like that: |
| 200 | +So if you had |
| 201 | +```jsx |
| 202 | +const { value, eventBind, valueBind, onChange, hasValue } = useInput("") |
| 203 | + |
| 204 | +<input value={value} onChange={onChange} /> |
| 205 | +<input {...eventBind} /> |
| 206 | +{hasValue && <Slider {...valueBind} />} |
| 207 | +``` |
| 208 | +It will become |
| 209 | +```jsx |
| 210 | +const [[value, hasValue], actions, { eventBind, valueBind }] = useBindToInput(useInput("")) |
| 211 | + |
| 212 | +<input value={value} onChange={actions.onChange} /> |
| 213 | +<input {...eventBind} /> |
| 214 | +{hasValue && <Slider {...valueBind} />} |
| 215 | +``` |
| 216 | + |
| 217 | +Note that first element in destructured array has tuple of `[value, hasValue]` since it's for values |
| 218 | +and second argument is for `actions` e.g. only for functions. |
0 commit comments