🥦
FunctionalComponentでsetStateのHookを実現する。
コード
カスタムフック
import * as React from 'react';
type Callback<T> = (newState: T) => void;
export type ObservingDispatcher<T> = (newState: T, observer?: Callback<T>) => void;
const useObserverbleState = <T>(initialState: T): [T, ObservingDispatcher<T>] => {
const [state, setState] = React.useState<T>(initialState);
const callbackRef = React.useRef<Callback<T> | undefined>(undefined);
const setObserverbleState = React.useCallback((newState: T, callback?: Callback<T>) => {
callbackRef.current = callback;
setState(newState);
}, []);
React.useEffect(() => {
if (callbackRef.current) {
callbackRef.current(state);
callbackRef.current = undefined;
}
}, [state]);
return [state, setObserverbleState];
};
export default useObserverbleState;
使い方
import useObserverbleState from '@/scripts/hooks/useObserverbleState';
import * as React from 'react';
export const Component: React.FC = () => {
const [value, setValue] = useObserverbleState(0);
return (
<button
onClick={() => {
setValue(value + 1, newValue => {
console.log(newValue);
});
}}
>
{value}
</button>
);
};
解説
検索するとuseEffect
を使った例がよく出てきます。
import * as React from 'react';
export const Component: React.FC = () => {
const [value, setValue] = React.useState(0);
React.useEffect(() => console.log(value), [value]);
return <button onClick={() => setValue(value + 1)}>{value}</button>;
};
これだとClassComponent時代のthis.setState
のHookと違ってコンポーネントがマウントした時にも実行されてしまいます。それでも問題ない時も多いとは思いますが、今回は実行したくなかったのでこのような実装をしました。
下記の回答からほとんどいただいたコードですが、TypeScriptが自分の設定と違うのか、Reactのバージョンのせいかわかりませんが、エラーが出たので書き換えました。
Discussion