render hooks pattern
>Custom Hook から Component を返却する render hooks パターンの場合、返却された Component がアンマウントされてしまうことによりバグが発生する恐れがあるので注意が必要です。
> Custom Hook から Component を返さずに React element を返す実装にすれば、 アンマウントを回避できます。
componentは含めないのか
ロジックとcomponentが明らかに凝集している、という場合に有用
Modal
というComponentを返している
tsconst App = () => {
const [Modal, open, close, isOpen] = useModal('root', {
preventScroll: true,
closeOnOverlayClick: false
});
return (
<div>
<p>Modal is Open? {isOpen ? 'Yes' : 'No'}</p>
<button onClick={open}>OPEN</button>
<Modal>
<div>
<h1>Title</h1>
<p>This is a customizable modal.</p>
<button onClick={close}>CLOSE</button>
</div>
</Modal>
</div>
);
};
実装箇所
useCallback使ってる
うまく動かない例
tsconst useForm = () => {
const [value, setValue] = useState('');
const Field: React.FC = useCallback(
() => <input value={value} onChange={e => setValue(e.target.value)} />,
[value],
);
return {
Field,
};
};
値が入力されるたびに Field
が新しく作られるので、1文字入力するたびにフォーカスが外れてしまう
どうしてもこういうAPIを提供したいならuseRef使ってハックっぽくするとか
tsconst useForm = () => {
const valueRef = useRef('');
const Field: React.FC = () => {
const inputRef = useRef<HTMLInputElement | null>(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.value = valueRef.current;
}
}, []);
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
valueRef.current = e.target.value;
};
return <input ref={inputRef} onChange={onChange} />;
};
return {
Field,
};
};
ちなみにこうかくとめちゃくちゃバグる
tsconst useForm = () => {
const [value, setValue] = useState('');
const ref = useRef(value);
useEffect(() => {
ref.current = value;
}, [value]);
const Field: React.FC = useCallback(
() => (
<input value={ref.current} onChange={(e) => setValue(e.target.value)} />
),
[]
);
return {
Field,
};
};