当前位置:网站首页>Why useevent is not good enough
Why useevent is not good enough
2022-06-24 18:46:00 【Code Taoist】

This article will make you believe useEvent RFC It is not a perfect solution to the problem of functional component callback . I will explain useEvent Disadvantages and pitfalls of , And provide better solutions .
useEvent RFC
This is a RFC Link to . It is strongly recommended that you read... Before reading this article RFC.
stay RFC in ,useEvent To solve 2 A question :
- Provide a stable identity Callback , So get the event callback prop Components of will not be re rendered .
- Get the new value asynchronously in the callback . Solve the problem of capturing obsolete values in closures .
however ,useEvent These two problems have not been well solved .
The stability of the identity Callback
useEvent Can provide stable identity Callback .useEvent RFC An example of :
function Chat() {
const [text, setText] = useState('');
const onClick = useEvent(() => {
sendMessage(text);
});
return <SendButton onClick={onClick} />;
}Better solution
try react-with-stable:
import { withStable } from "react-with-stable";
const SendButton = withStable(
["onClick"],
({ onClick }) => (
<button onClick={onClick}>Click</button>
)
); Use withStable HOC packing SendButton, And clearly state onClick** It's a stable one prop. That's it ! This is the code you have to add . There is no need to wrap the event callback !!!!!!!!!!!!
please remember , Even if you use useEvent, You still have to use React.memo To wrap components to eliminate unnecessary re rendering .
Let's compare :
import { withStable } from "react-with-stable";
const SendButton = withStable(
["onClick"], // list 'onClick' as stable callback
({ onClick }) => (
<button onClick={onClick}>Click</button>
)
);
function Chat() {
const [text, setText] = useState('');
// No need to wrap it
const onClick = () => {
sendMessage(text);
};
return <SendButton onClick={onClick} />;
}
// vs.
const SendButton = React.memo(
({ onClick }) => (
<button onClick={onClick}>Click</button>
)
);
function Chat() {
const [text, setText] = useState('');
// Still have to write useEvent every time
const onClick = useEvent(() => {
sendMessage(text);
});
return <SendButton onClick={onClick} />;
}useEvent The shortcomings and withStable The advantages of :
1. [ `useEvent`] Cannot wrap inline callbacks
function Chat({rooms}) {
const [text, setText] = useState('');
return rooms.map(room => (
<SendButton onClick={
() => sendMessage(room, text)
// Can't wrap it with useEvent
} />
));
} It is common to render component lists in a loop . We use closures to pass item Callback . however , It is impossible to use useEvent, Because of anything hook Can't be in loop and Conditions Use in .
1. [ `withStable`] You can use inline callbacks ( For example, circulation )
function Chat({rooms}) {
const [text, setText] = useState('');
return rooms.map(room => (
<SendButton onClick={
() => sendMessage(room, text)
// No re-render and no wrapping
} />
));
}2. [ `useEvent`] Use each time Event, Easy to forget
function ChatA({text}) {
const onClick = useEvent(() => sendMessage(text));
// 1st time
return <SendButton onClick={onClick} />;
}
function ChatB({text}) {
const onClick = useEvent(() => sendMessage(text));
// 2nd time
return <SendButton onClick={onClick} />;
}
function ChatC({text}) {
const onClick = useEvent(() => sendMessage(text));
// 3rd time
return <SendButton onClick={onClick} />;
} It's like useCallback, In order to optimize the , We must use it every time useEvent For packaging . This is in RFC Of shortcoming There are instructions in the section :
Compared with ordinary event handlers , packing
useEventIt seems more troublesome .
actually ,useEvent The cost of computing and memory is very low —— Its real cost is the development cost ( namely DX), This requires developers to remember and write code .
2. [`withStable`] Pack once , Optimize everywhere
const SendButton = withStable(["onClick"], ({ onClick }) => (
<button onClick={onClick}>click</button>
));
function ChatA({text}) {
const onClick = () => sendMessage(text);
// No need to do anything else
return <SendButton onClick={onClick} />;
}
function ChatB({text}) {
const onClick = () => sendMessage(text);
// No need to do anything else
return <SendButton onClick={onClick} />;
}
function ChatC({text}) {
const onClick = () => sendMessage(text);
// No need to do anything else
return <SendButton onClick={onClick} />;
} If you now have a subcomponent to optimize , Yes 10 Parent components are using it , Then you have to rewrite it useEvent10 Time !
by comparison , Use withStable When , Subcomponents only need to be packaged once , All parent components will be optimized .
That's what we're talking about DX.
3. [ `useEvent`] It doesn't help the condition value
function Chat({onOdd, onEven}) {
const [text, setText] = useState('');
return <SendButton
onClick={text.length % 2 ? onEven : onOdd}
// React.memo fails, and it'll re-render
/>;
}onOdd and onEven It is stable. , But the conditions text.length % 2 Constant change , therefore SendButton Can't get the same... That caused the re rendering identity Callback . This is in RFC Of shortcoming There are instructions in the section .
To solve this problem , We have to define a term that is defined by useEvent New callback for packaging .
3. [ `withStable`] It also applies to conditional values
function Chat({onOdd, onEven}) {
const [text, setText] = useState('');
return <SendButton
onClick={text.length % 2 ? onEven : onOdd}
// SendButton won't re-render
/>;
}4. [ `useEvent`] It is a burden for users who use the library
import { Form } from 'antd';
function Chat({onOdd, onEven}) {
const [text, setText] = useState('');
const onSubmit = useEvent(() => {
sendMessage(text);
});
return <Form submit={onSubmit} />;
// 🧐 Is 'submit' prop useEvent-safe?
}Library users need to know which callbacks prop yes useEvent-safe Of ( Use... Only in event handlers , Instead of using in rendering ). If you use... In rendering useEvent Packaged submit stay render Use in ,React Will report a mistake .
4. [ `withStable`] Will perform performance optimization
import { Form } from 'antd';
// If 'submit' prop is useEvent-safe,
// antd would wrap withStable (from React.memo) internally
function Chat({onOdd, onEven}) {
const [text, setText] = useState('');
const onSubmit = () => {
sendMessage(text);
};
return <Form submit={onSubmit} />;
// I don't care!
}Get the new value in the callback
useEventRFC We also want to solve the problem of over time values in closures , As shown in the following example :
function Chat({ selectedRoom }) {
const theme = useContext(ThemeContext);
const onConnected = useEvent(connectedRoom => {
showToast(theme, 'Connected to ' + connectedRoom);
});
useEffect(() => {
const socket = createSocket('/chat/' + selectedRoom);
socket.on('connected', async () => {
await checkConnection(selectedRoom);
onConnected(selectedRoom);
});
}, [selectedRoom]);
} One thing to note is that , Only when the call is not all synchronized , The value in the callback may become obsolete , such as stay await Or the callback is invoked asynchronously .
useEvent The pattern of getting new values has some pitfalls and drawbacks :
1. need effects Define a new callback
// ...
socket.on('connected', async () => {
await checkConnection(selectedRoom);
showToast(theme, 'Connected to ' + connectedRoom);
// I just want to call showToast with one line
// Why I must define a new callback with 3 lines
// of code outside of effect?? 🤨
});2. cannot access effects Internal value
Suppose we define a new variable count, And hope in every tick Add it .
function Note() {
const [text, setText] = useState('');
const onTick = useEvent(() => {
count += 1; // I can't modify 'count'
saveDraft(text, count);
});
useEffect(() => {
let count = 0;
const id = setInterval(onTick, 1000);
return () => clearInterval(id);
}, []);
} This means that if we want to use useEvent, We need to give up JavaScript The benefits of closures !useEvent Force us to jump out of the current closure to a closure by useEvent In the component level callback of the package .
3. Still can't get a new value
function Chat({ selectedRoom }) {
const theme = useContext(ThemeContext);
const onConnected = useEvent(async connectedRoom => {
showToast(theme, 'Checking connection to ' + connectedRoom);
await checkConnection(selectedRoom);
showToast(theme, 'Connected to ' + connectedRoom);
// After await, theme is not fresh!
});
useEffect(() => {
const socket = createSocket('/chat/' + selectedRoom);
socket.on('connected', async () => {
onConnected(selectedRoom);
});
}, [selectedRoom]);
}This is a very serious trap . stay await Or after an asynchronous callback , It's easy not to know that some values are not new . This is in RFC Of shortcoming There are instructions in the section .
Define a new onChecked The callback looks worse to me .
function Chat({ selectedRoom }) {
const theme = useContext(ThemeContext);
const onChecked = useEvent(async connectedRoom => {
showToast(theme, 'Connected to ' + connectedRoom);
});
const onConnected = useEvent(async connectedRoom => {
showToast(theme, 'Checking connection to ' + connectedRoom);
await checkConnection(selectedRoom);
onChecked(selectedRoom);
});
useEffect(() => {
const socket = createSocket('/chat/' + selectedRoom);
socket.on('connected', async () => {
onConnected(selectedRoom);
});
}, [selectedRoom]);
}Personally, I don't like the code above . It lost its use async/await The meaning of grammar .
Better solution
Try this. npm package :react-better-effect
import { useEffect } from "react-better-effect";
function Chat({ selectedRoom }) {
const theme = useContext(ThemeContext);
useEffect(($) => {
const socket = createSocket('/chat/' + selectedRoom);
socket.on('connected', async () => {
await checkConnection(selectedRoom);
showToast($.theme, 'Connected to ' + selectedRoom);
// get the latest 'theme' value from $
});
}, [selectedRoom], {theme}); // inject 'theme' here
}Advantage is :
1. No need to effects Define a new callback
// ...
socket.on('connected', async () => {
await checkConnection(selectedRoom);
showToast($.theme, 'Connected to ' + selectedRoom);
// One line to invoke showToast.
// No one wants to define a new callback.
});2. You can access values by staying in the current closure
function Note() {
const [text, setText] = useState('');
useEffect(($) => {
let count = 0;
const id = setInterval(() => {
count += 1; // Just modify count
saveDraft($.text, count);
}, 1000);
return () => clearInterval(id);
}, [], {text});
}3. Always get new values asynchronously
function Chat({ selectedRoom }) {
const theme = useContext(ThemeContext);
useEffect(($) => {
const socket = createSocket('/chat/' + selectedRoom);
socket.on('connected', async () => {
showToast($.theme, 'Checking connection to ' + connectedRoom);
await checkConnection(selectedRoom);
showToast($.theme, 'Connected to ' + connectedRoom);
// $.theme is fresh anytime
});
}, [selectedRoom], {theme});
}Generalization
useEvent Can solve React Some problems of callback in , But there are still some unresolved issues that lead to pitfalls . I can imagine useEvent Will result in a lot of unnecessary refactoring , For example, move event handler closures and effect closures to useEvent. It makes the code more ugly , But it still can't solve all the problems of callback .
withStable HOC Can solve “ I should use it everywhere useCallback Do you ?” This confusion . In my rough estimate ,React Application 90% The callback of is an event callback . If you use withStable, We can reduce 90% useEvent even to the extent that useCallback Use .
边栏推荐
- Learn routing and data delivery
- Keep two decimal places
- 使用阿里云RDS for SQL Server性能洞察优化数据库负载-初识性能洞察
- Common MySQL commands of installation free version
- 如何在 R 中创建线性模型预测区间 并可视化
- Air pollution gas satellite data download tutorial
- Selection (030) - what is the output of the following code?
- Set up your own website (8)
- 应用程序DDoS攻击原理及防御方法
- What is decision intelligence?
猜你喜欢

ASP. Net hosting uploading file message 500 error in IIS

微服务系统设计——数据模型与系统架构设计

Redis learning -- list of redis operations
![subject may not be empty [subject-empty]](/img/6b/9b57a7ed3ab086036cb6dfe0b31de4.png)
subject may not be empty [subject-empty]

JS pre parsing

Microservice system design -- data model and system architecture design

Network security database penetration of secondary vocational group in 2022

Vite+web3: referenceerror: process is not defined

Analysis on the issue of raising the right of MSSQL in 2021 secondary vocational group network security competition in Shandong Province

Value passing and reference passing of value types and reference types in CSharp
随机推荐
Conception de systèmes de micro - services - construction de sous - services
Is it safe to open an account online? What should I do?
Wechat applet to realize stacked rotation
Interview algorithm - string question summary
SAP license: SAP s/4 Hana module function introduction
Paper sharing | self supervised learning paper jointly released by Yann Lecun and read by engineers
JS string method
Three layer switching experiment
next_ Permutation full permutation function
Flutter dart regular regexp matches non printing characters \cl\cj\cm\ck
Data modeling technology of Business Intelligence BI
Redis learning -- list of redis operations
论文解读(SR-GNN)《Shift-Robust GNNs: Overcoming the Limitations of Localized Graph Training Data》
Leetcode topic [array] -216- combined sum III
微服務系統設計——子服務項目構建
Flex box flex attribute
建立自己的网站(8)
如何在 R 中使用 Fisher 的最小显着性差异 (LSD)
微服务系统设计——子服务项目构建
Make track map