gpt4 book ai didi

javascript - react 性能: What is a good strategy to maintain constant references for event handler props?

转载 作者:行者123 更新时间:2023-12-02 21:45:53 25 4
gpt4 key购买 nike

假设我有一个缓慢的组件,那么记住它是有意义的,例如

const SlowButton = ({onClick}) => {
// make some heat
const value = (function f(n) { return n < 2 ? Math.random() : f(n-1) + f(n-2)})(32)|0;
return <button onClick={() => onClick(value)}>{value}</button>
}
const MemoButton = React.memo(SlowButton);

如果我在组件中使用 MemoButton,例如:

const Counter = () => {
const [clicks, setClicks ] = useState(0);
const handleClick = () => {
setClicks(clicks + 1);
}
return <div>
<div>{clicks}</div>
<MemoButton onClick={handleClick} />
</div>
}

然后 MemoButton 每次仍然会重新渲染,因为 onClick 属性每次渲染都是一个新函数。解决这个问题非常简单:

const Counter2 = () => {
const [clicks, setClicks] = useState(0);
const handleClick = useCallback(() => {
setClicks(c => c + 1);
},[]);

return <div>
<div>{clicks}</div>
<MemoButton onClick={handleClick} />
</div>
}

上面的代码工作得很好,但是对于更复杂的组件,它就不太好用了:

const CounterGroup = () => {
const numButtons = 3;
const [message, setMessage] = useState('button X clicked');
const handleClick = (val, idx) => setMessage(`button ${idx} clicked: ${val}`);
return <div>
<div>{message}</div>
{Array(numButtons).fill(0).map((_, i) =>
<MemoButton key={i} onClick={(v) => handleClick(v,i)} />)
}
</div>
}

在上面的代码中,(v) => handleClick(i,v) 始终是一个新的函数引用。 有没有一种好的技术可以防止它改变每个渲染?

一种可能性是忽略对 'on...' 属性的更改,但这只会产生新问题:

const compareValuePropsOnly = (prev, next) => Object.entries(prev).every(
([k, v]) => k.substr(0, 2) === "on" || v === next[k]
);
const MemoOnlyValsButton = React.memo(SlowButton, compareValuePropsOnly);

这是一个codesandbox版本: https://codesandbox.io/s/memoization-function-reference-changes-9c1fy

最佳答案

一种解决方案是让您的 SlowButton 传递 i 值,而不是从循环中获取它,并记住 handleClick

const SlowButton = ({onClick, i}) => {
// make some heat
const value = (function f(n) { return n < 2 ? Math.random() : f(n-1) + f(n-2)})(32)|0;
return <button onClick={() => onClick(value, i)}>{value}</button>
}
const MemoButton = React.memo(SlowButton);

const CounterGroup = () => {
const numButtons = 3;
const [message, setMessage] = useState('button X clicked');
const handleClick = React.useCallback((val, idx) => setMessage(`button ${idx} clicked: ${val}`), []);
return <div>
<div>{message}</div>
{Array(numButtons).fill(0).map((_, i) =>
<MemoButton key={i} i={i} onClick={handleClick} />)
}
</div>
}

另一种方法是在 React.memo 中排除“onClick”属性(因为它是一个事件处理程序,所以它不应该影响组件的外观)。

  const MemoButton = React.memo(SlowButton, (props1, props2) => {
// assume that SlowButton have some props that affect it's UI
// we don't compare onClick because it won't affect UI
return props1.someUIProps === props2.someUIProps;
})

或者您可以使用 useEventCallback Hook 来记录您的函数。在这种情况下,您需要在 CounterGroupMemoButton

之间创建一个组件
const useEventCallback = (callback) => {
// store latest callback
const ref = useRef(callback);
useEffect(() => ref.current = callback);

// memoize the callback to maintain its identity
return useCallback((...args) => ref.current(...args), []);
}
const FastButton = ({onClick} => {
// FastButton will be re-rendered multiple times, but since memoOnClick have same identity
// on sub sequence re-renders, MemoButton should not be re-rendered
const memoOnClick = useEventCallback(onClick);

return <MemoButton onClick={memoOnClick} />
});


const CounterGroup = () => {
const numButtons = 3;
const [message, setMessage] = useState('button X clicked');
const handleClick = (val, idx) => setMessage(`button ${idx} clicked: ${val}`);
return <div>
<div>{message}</div>
{Array(numButtons).fill(0).map((_, i) =>
<FastButton key={i} onClick={(v) => handleClick(v,i)} />)
}
</div>
}

关于javascript - react 性能: What is a good strategy to maintain constant references for event handler props?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60251762/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com