1- React Toolbox
2- =============
1+ # React Toolbox
32
43[ ![ npm package] ( https://badge.fury.io/js/%40aboutbits%2Freact-toolbox.svg )] ( https://badge.fury.io/js/%40aboutbits%2Freact-toolbox )
54[ ![ license] ( https://img.shields.io/github/license/aboutbits/react-toolbox )] ( https://github.com/aboutbits/react-toolbox/blob/main/license.md )
@@ -13,6 +12,8 @@ This package includes different tools that support you with common tasks.
1312 - [ Async Data] ( #async-data )
1413 - [ LocationProvider] ( #locationprovider )
1514 - [ useMatchMediaQuery] ( #usematchmediaquery )
15+ - [ useDebounce] ( #usedebounce )
16+ - [ useIsMounted] ( #useismounted )
1617- [ Build & Publish] ( #build--publish )
1718- [ Information] ( #information )
1819
@@ -41,14 +42,17 @@ import { useInterval } from '@aboutbits/react-toolbox'
4142
4243const MyCommponent = () => {
4344 const [step, setStep] = useState (10 )
44-
45- useInterval (() => {
46- setStep (step - 1 )
47- }, step === 0 ? null : 1000 )
45+
46+ useInterval (
47+ () => {
48+ setStep (step - 1 )
49+ },
50+ step === 0 ? null : 1000
51+ )
4852
4953 return <p >Countdown: { step } </p >
5054}
51- ```
55+ ```
5256
5357### Async Data
5458
@@ -59,31 +63,32 @@ import React, { useEffect } from 'react'
5963import { AsyncView } from ' @aboutbits/react-toolbox'
6064
6165type Data = {
62- greeting: string
66+ greeting: string
6367}
6468
6569type Error = {
66- message: string
70+ message: string
6771}
6872
6973const MyCommponent = () => {
70- const [data, setData] = useState <Data | undefined >()
71- const [error, setError] = useState <Error | undefined >()
74+ const [data, setData] = useState <Data | undefined >()
75+ const [error, setError] = useState <Error | undefined >()
7276
73- useEffect (() => {
74- fetch (' https://jsonplaceholder.typicode.com/todos/1' )
75- .then (response => setData (response .json ()))
76- .catch (error => setError (error ))
77- })
77+ useEffect (() => {
78+ fetch (' https://jsonplaceholder.typicode.com/todos/1' )
79+ .then (( response ) => setData (response .json ()))
80+ .catch (( error ) => setError (error ))
81+ })
7882
79- return (
80- <AsyncView
81- data = { data }
82- error = { error }
83- renderLoading = { <div >Loading</div >}
84- renderSuccess = { (data ) => <div >{ data .greeting } </div >}
85- renderError = { (error ) => <div >{ error .message } </div >} />
86- );
83+ return (
84+ <AsyncView
85+ data = { data }
86+ error = { error }
87+ renderLoading = { <div >Loading</div >}
88+ renderSuccess = { (data ) => <div >{ data .greeting } </div >}
89+ renderError = { (error ) => <div >{ error .message } </div >}
90+ />
91+ )
8792}
8893```
8994
@@ -95,26 +100,27 @@ import { useSWR } from 'swr'
95100import { AsyncView } from ' @aboutbits/react-toolbox'
96101
97102type Data = {
98- greeting: string
103+ greeting: string
99104}
100105
101106type Error = {
102- message: string
107+ message: string
103108}
104109
105110const MyCommponent = () => {
106- const { data, error } = useSWR (' https://jsonplaceholder.typicode.com/todos/1' )
107-
108- return (
109- <AsyncView
110- data = { data }
111- error = { error }
112- renderLoading = { ' Loading' }
113- renderSuccess = { ' Success' }
114- renderError = { ' Error' } />
115- );
111+ const { data, error } = useSWR (' https://jsonplaceholder.typicode.com/todos/1' )
112+
113+ return (
114+ <AsyncView
115+ data = { data }
116+ error = { error }
117+ renderLoading = { ' Loading' }
118+ renderSuccess = { ' Success' }
119+ renderError = { ' Error' }
120+ />
121+ )
116122}
117- ```
123+ ```
118124
119125### LocationProvider
120126
@@ -124,7 +130,6 @@ This part includes a React context that fetches the geolocation at a given inter
124130import { LocationProvider } from ' @aboutbits/react-toolbox'
125131
126132const MyApp = () => {
127-
128133 return (
129134 <LocationProvider highAccuracy = { true } delay = { 20000 } >
130135 { children }
@@ -134,6 +139,7 @@ const MyApp = () => {
134139```
135140
136141The context provider takes two props:
142+
137143- ` highAccuracy ` : defines if the location should be fetched with high accuracy. Read more on the [ Geolocation API doc] ( https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API ) .
138144- ` delay ` : the delay in milliseconds between each fetch
139145
@@ -143,10 +149,14 @@ import { LocationContext } from '@aboutbits/react-toolbox'
143149
144150const MyComponent = () => {
145151 const { location } = useContext (LocationContext )
146-
147- return location
148- ? <div >Your location is: { location .coords .latitude } , { location .coords .longitude } </div >
149- : <div >Unable to get your location</div >
152+
153+ return location ? (
154+ <div >
155+ Your location is: { location .coords .latitude } , { location .coords .longitude }
156+ </div >
157+ ) : (
158+ <div >Unable to get your location</div >
159+ )
150160}
151161```
152162
@@ -164,6 +174,76 @@ const TestComponent = () => {
164174}
165175```
166176
177+ ### useDebounce
178+
179+ Use this hook to prevent the component from re-rendering too many times. Useful to avoid making unnecessary API calls.
180+
181+ ``` tsx
182+ export default function TestComponent() {
183+ const [value, setValue] = useState (' ' )
184+ const debouncedValue = useDebounce (value , 500 )
185+
186+ const handleChange = (event : ChangeEvent <HTMLInputElement >) => {
187+ setValue (event .target .value )
188+ }
189+
190+ // Fetch API (optional)
191+ useEffect (() => {
192+ // Do fetch here...
193+ // Triggers when "debouncedValue" changes
194+ }, [debouncedValue ])
195+
196+ return (
197+ <div >
198+ <p >Value real-time: { value } </p >
199+ <p >Debounced value: { debouncedValue } </p >
200+ <input type = " text" value = { value } onChange = { handleChange } />
201+ </div >
202+ )
203+ }
204+ ```
205+
206+ ### useIsMounted
207+
208+ In React, a component is deleted from memory once unmounted. Changing the state in an unmounted component will result in an error.
209+ This is preferrably solved passing a cleanup function to [ useEffect] ( https://react.dev/reference/react/useEffect#useeffect ) .
210+ However, there are some cases like Promise or API calls where it's impossible to know if the component is still mounted at the resolve time.
211+ This hook returns a function that can be used to verify at the resolve time whether the component is still mounted.
212+
213+ ``` tsx
214+ const delay = (ms : number ) => new Promise ((resolve ) => setTimeout (resolve , ms ))
215+
216+ function Child() {
217+ const [data, setData] = useState (' loading' )
218+ const isMounted = useIsMounted ()
219+
220+ // simulate an api call and update state
221+ useEffect (() => {
222+ void delay (3000 ).then (() => {
223+ if (isMounted ()) {
224+ setData (' OK' )
225+ }
226+ })
227+ }, [isMounted ])
228+
229+ return <p >{ data } </p >
230+ }
231+
232+ export default function TestComponent() {
233+ const [isVisible, setVisible] = useState <boolean >(false )
234+
235+ const toggleVisibility = () => setVisible ((state ) => ! state )
236+
237+ return (
238+ <>
239+ <button onClick = { toggleVisibility } >{ isVisible ? ' Hide' : ' Show' } </button >
240+
241+ { isVisible && <Child />}
242+ </>
243+ )
244+ }
245+ ```
246+
167247## Build & Publish
168248
169249To publish the package commit all changes and push them to main. Then run one of the following commands locally:
0 commit comments