Today, I will introduce several React
hooks
, which are not commonly used, but are very useful.
useSyncExternalStore
listen for external data changes
external data sources need to provide a subscription function, and this function needs to return the method of unsubscribing
import React from 'react';
import { store } from './store.js'
export const App () {
const data = useSyncExternalStore(store.subscribe, store.getData)
return <>
<button onClick={store.add}>add+</button>
{data}
</>
}
// store.js
let conut = 0;
let listeners = [];
export const store = {
add () {
count ++;
},
subscribe(listener) {
listeners = [...listeners, listener];
return () => {
listeners = listeners.filter(l => l !== listener);
};
},
geetDate () {
return count;
}
}
useId
generate globally unique ID, give up Math.random ()
bar
import { useId } from 'react';
function App() {
const uuid = useId();
return (
<>{uuid}</>
);
}
useLayoutEffect
trigger before layout update
import { useState, useRef, useLayoutEffect } from 'react';
function Tooltip() {
const ref = useRef(null);
const [tooltipHeight, setTooltipHeight] = useState(0);
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipHeight(height);
}, []);
return <></>
}
useDeferredValue
UI defer updates without handwritten anti-shake function
import SearchResults from './SearchResults.js';
export default function App() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<SearchResults query={deferredQuery} />
</Suspense>
</>
);
}
useReducer
customize a lightweight redux
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
useRef
define a reference to a value
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useImperativeHandle
customize the handle exposed by ref
.
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} {..props} />;
}