我尝试使用react挂钩而不是基于类的组件,并且在性能方面存在一些问题。
代码:
import React, { memo, useCallback, useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
let counter = -1;
function useToggle(initialValue) {
const [toggleValue, setToggleValue] = useState(initialValue);
const toggler = useCallback(() => setToggleValue(!toggleValue), [
toggleValue,
setToggleValue
]);
return [toggleValue, toggler];
}
const Header = memo(({ onClick }) => {
counter = counter + 1;
return (
<div>
<h1>HEADER</h1>
<button onClick={onClick}>Toggle Menu</button>
<div>Extra Render: {counter}</div>
</div>
);
});
const Dashboard = memo(() => {
const [visible, toggle] = useToggle(false);
const handleMenu = useCallback(
() => {
toggle(!visible);
},
[toggle, visible]
);
return (
<>
<Header onClick={handleMenu} />
<div>Dashboard with hooks</div>
{visible && <div>Menu</div>}
</>
);
});
export default Dashboard;
以下是我想做的一个例子:Example。
如您所见,Header组件中还有其他渲染器。 我的问题:是否可以避免使用react-hooks的额外渲染?
答案 0 :(得分:2)
将您的自定义钩子useToggle
更改为使用功能状态设置器,例如
function useToggle(initialValue) {
const [toggleValue, setToggleValue] = useState(initialValue);
const toggler = useCallback(() => setToggleValue(toggleValue => !toggleValue));
return [toggleValue, toggler];
}
并像这样使用它:
const Dashboard = memo(() => {
const [visible, toggle] = useToggle(false);
const handleMenu = useCallback(
() => {
toggle();
}, []
);
return (
<>
<Header onClick={handleMenu} />
<div>Dashboard with hooks</div>
{visible && <div>Menu</div>}
</>
);
});
完整示例:https://codesandbox.io/s/z251qjvpw4
这可能更简单(感谢@DoXicK)
function useToggle(initialValue) {
const [toggleValue, setToggleValue] = useState(initialValue);
const toggler = useCallback(() => setToggleValue(toggleValue => !toggleValue), [setToggleValue]);
return [toggleValue, toggler];
}
const Dashboard = memo(() => {
const [visible, toggle] = useToggle(false);
return (
<>
<Header onClick={toggle} />
<div>Dashboard with hooks</div>
{visible && <div>Menu</div>}
</>
);
});
答案 1 :(得分:0)
如果使用回调模式更新状态,则可以避免额外的重新渲染,因为无需一次又一次地创建函数,而只需在第一次渲染时创建handleMenu
const Dashboard = memo(() => {
const [visible, toggle] = useToggle(false);
const handleMenu = useCallback(() => {
toggle(visible => !visible);
}, []);
return (
<>
<Header onClick={handleMenu} />
<div>Dashboard with hooks</div>
{visible && <div>Menu</div>}
</>
);
});
答案 2 :(得分:0)
这是useCallback
经常失效的问题。 (在此处的{@ {3}}的React repo上有关于此的对话)
因为每次useCallback
值更改时toggle
将失效并返回一个新函数,然后将一个新的handleMenu
函数传递给<Header />
导致它重新呈现。 / p>
一种解决方法是创建自定义useCallback
挂钩:
(从https://github.com/facebook/react/issues/14099复制)
function useEventCallback(fn) {
let ref = useRef();
useLayoutEffect(() => {
ref.current = fn;
});
return useMemo(() => (...args) => (0, ref.current)(...args), []);
}
示例:https://github.com/facebook/react/issues/14099#issuecomment-457885333