React奇奇怪怪技巧之內(nèi)聯(lián)hook
本文介紹一種 react 當(dāng)中在一堆 jsx 中嵌入 hook 的方式 inlineHook。
場景 or 為什么
一、列表渲染
列表渲染中,你或許需要為每個(gè)元素準(zhǔn)備其狀態(tài)和副作用,這時(shí)候你就需要用一個(gè)新的組件,如果需要用到父組件的狀態(tài),還需要透傳一堆 props 給它。也就是說,你為了使用一個(gè)組件,需要包一個(gè)新的組件出來。
function Parent(){
// states
// effects
return <>
{list.map(item =>
<ChildComponentWrap
key={item.key}
// ...state
// ...callback
// 這里其實(shí)可能很多
/>
)}
</>
}
// 一個(gè)黑盒組件
function ChildComponent(props){
return <>...</>
}
// 為了維護(hù)它的狀態(tài)而包裝的組件
function ChildComponentWrap(props){
// states
// effects
return <ChildComponent
// ...state
// ...callback
/>
}作為一個(gè)很擅長堆屎山的人,嘗試過下面這樣的代碼,但是由于 react hook 的固定規(guī)則,元素?cái)?shù)量發(fā)生變化的時(shí)候,必然報(bào)錯(cuò)。(相信一些 react 新手肯定掉過這個(gè)坑)
function Parent(){
// states
// effects
return <>
{list.map(item =>{
useState()
useEffect(()=>{}, [xxx]);
return <ChildComponentWrap
key={item.key}
// ...state
// ...callback
// 這里其實(shí)可能很多
/>
}
)}
</>
}所以需要某種方法,讓它支持內(nèi)聯(lián) hook,就有了 inlineHook 的這樣一個(gè)工具,原理很簡單,它創(chuàng)造了一個(gè)組件,并把你的代碼塊運(yùn)行在里面,所以里面可以用 hook,當(dāng)?shù)谝粎?shù)為 string 的時(shí)候,作為組件的 key。
function Parent(){
// states
// effects
return <>
{list.map(item =>
inlineHook(item.key, () => {
useState()
useEffect(()=>{}, [xxx]);
return <ChildComponent
// ...state
// ...callback
/>
})
)}
</>
}二、條件副作用
錯(cuò)誤代碼
function AComponent(){
// 注意這里,condition false true 轉(zhuǎn)換的時(shí)候會(huì)報(bào)錯(cuò)
if(condition){
useEffect(()=>{}, [])
}
return <>...</>
}使用 inlineHook,基于條件掛載組件的方式就可以實(shí)現(xiàn)條件 hook。
function AComponent(){
return <>
{condition && inlineHook(()=>{
useEffect(()=>{}, []);
})}
...
</>
}再考慮一個(gè)這樣的 case:有個(gè)長鏈推送文本,當(dāng)文本變化的時(shí)候,將文本變?yōu)榫G色,持續(xù) 3s 之后恢復(fù)黑色。在你可以接受的情況下,何必為了小小一個(gè) text 變色去創(chuàng)建一個(gè)組件呢?
const text = useReceiveMessage();
return <>
<div>別的什么東西</div>
{
text && inlineHook(text, () => {
const { value } = useCountdown(3);
return <span style={{ color: value ? 'green' : '#000' }}>
{text}
</span>
})
}
<div>別的什么東西</div>
<>三、表單聯(lián)動(dòng)
想象這樣一個(gè)場景,你有一個(gè)條件表單。
- A 會(huì)限制 B 的選項(xiàng)
- A 選 'a' 時(shí),B 可選 '1'、'2'、'3';
- A 選 'b' 時(shí),B 可選 '3'、'4'、'5';
- A 選 'c' 時(shí),B 可選 '5'、'6'、'7';
- 當(dāng) A 切換的時(shí)候,你需要清除已選中的 B選項(xiàng)(注意上面的 B可選項(xiàng)是有重疊的,有些值可以保留)
return <Form>
<Form.Select
field='A'
optionList={['a','b','c']}
/>
{* 可能離得很遠(yuǎn) *}
<Form.Select
field='B'
multiple // 多選
optionList={[...]} // 想辦法映射一下選項(xiàng)
/>
</Form>思考一下,怎么弄。
可以在 A 的 onChange 里面處理一下
<Form.Select
field='A'
optionList={['a','b','c']}
onChange={(v) => { // 可以提出去,但是邏輯也很跳躍
const BOptionList = getBOptionList(v);
const newValues = ...
const values = formApi.getValue('B')
if(!isEqual(values,newValues)){
formApi.setValue('B', newBValue)
}
}}
/>
{* 可能離得很遠(yuǎn),不一定會(huì)關(guān)注到這兩個(gè)表單項(xiàng)的關(guān)系 *}
<Form.Select
field='B'
multiple // 多選
optionList={[...]} // 想辦法映射一下選項(xiàng)
/>也可以拆組件,對嗎?如我之前所說,你需要在組件間跳躍,上下文 props 需要傳遞。
function FieldB(props){
const AValue = useFieldValue("A");
const formApi = useFormApi();
const BOptionList = getBOptionList(AValue);
useEffect(() => {
const values = formApi.getValue("B");
// 檢查一下 B 的值在不在 optionList 里面,不在就清除掉
const newValues = ... // 想辦法處理出來
if(!isEqual(values, newValues)){
form.setValue("B", newValues);
}
}, [AValue]);
return <Form.Select
field='B'
optionList={BOptionList}
/>
}使用 inlineHook 之后,大概就是這樣。這并不是說這是什么好辦法,這是一個(gè)偏好問題,但至少,不需要一個(gè)新的組件(當(dāng)你需要頻繁修改的時(shí)候,過多的組件跳轉(zhuǎn)會(huì)影響編碼效率),組件的使用與聯(lián)動(dòng)關(guān)系可以放得近。
return <Form>
<Form.Select
field='A'
optionList={['a','b','c']}
/>
{* 可能離得很遠(yuǎn) *}
{inlineHook(()=>{
const AValue = useFieldValue("A");
const formApi = useFormApi();
useEffect(() => {
const BOptionList = getBOptionList(v);
const values = formApi.getValue("B");
// 檢查一下 B 的值在不在 optionList 里面,不在就清除掉
const newValues = ... // 想辦法處理出來
if(!isEqual(values, newValues)){
form.setValue("B", newValues);
}
}, [AValue]);
return <Form.Select
field='B'
optionList={[...]} // 想辦法映射一下選項(xiàng)
/>
}}
</Form>四、類組件里面不能使用 hook 嗎
雖然這年頭應(yīng)該沒有什么人會(huì)用類組件了,如下,這個(gè)用法也是寫文章的時(shí)候才想起來的,我覺得是可行的。
class AComponent extends Component{
state = {
a: 1
}
render(){
return inlineHook(() => {
const [b, setB] = useState(2);
this.state.a;
useEffect(() => {
}, [this.state.a, b])
return <>...</>
})
}
}用法
return <>
{* 直接嵌入 *}
{inlineHook(()=>{
useState();
useEffect(()=>{}, []);
return <>可以返回elem渲染</>
})}
{* 條件hook *}
{
condition && inlineHook(()=>{
useEffect(()=>{},[])
return <>可以返回elem渲染</>
})
}
{* 列表 *}
{
list.map(item=>
inlineHook(item.key, ()=>{
useState();
useEffect(()=>{}, []);
return <>可以返回elem渲染</>;
}))
}
</>好處
- 減少一次性 Wrapper 組件
- 在編碼早期,頻繁修改代碼的時(shí)候,可以盡快驗(yàn)證,而不需要?jiǎng)?chuàng)建一個(gè)新的組件
壞處
- 你需要繞過這個(gè)文件的 react-hook eslint 檢查,破壞了規(guī)范
- 代碼會(huì)越寫越長,人會(huì)越來越懶(用爽了都不想重構(gòu)了)
源碼
import React, { createElement, isValidElement } from 'react';
function Hook({ fn }: { fn: () => any }) {
const res = fn();
return isValidElement(res) ? res : null;
}
const NullComponent = () => null;
/**
* 掛載 hook,當(dāng)你不想為它寫一個(gè)組件或者不知掛哪個(gè)組件上的時(shí)候
* @param fn 傳入一個(gè) hook 函數(shù)
* @returns
*/
export function inlineHook(fn: () => any): React.ReactElement;
export function inlineHook(key: string, fn: () => any): React.ReactElement;
export function inlineHook(...args: any[]) {
const [a, b] = args;
if (typeof a === 'function') {
return createElement(Hook, {
fn: a,
});
} else if (typeof a === 'string') {
return createElement(Hook, {
key: a,
fn: b,
});
} else {
return createElement(NullComponent, {});
}
}總結(jié)
到此這篇關(guān)于React內(nèi)聯(lián)hook的文章就介紹到這了,更多相關(guān)React內(nèi)聯(lián)hook內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react-beautiful-dnd拖拽排序功能的實(shí)現(xiàn)過程
這篇文章主要介紹了react-beautiful-dnd拖拽排序功能的實(shí)現(xiàn)過程,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
Express+React+Antd實(shí)現(xiàn)上傳功能(前端和后端)
這篇文章主要介紹了Express+React+Antd實(shí)現(xiàn)上傳功能(前端和后端),本文通過示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-04-04
使用Jenkins部署React項(xiàng)目的方法步驟
這篇文章主要介紹了使用Jenkins部署React項(xiàng)目的方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03
React庫之react-beautiful-dnd介紹及其使用過程
在使用React構(gòu)建Web應(yīng)用程序時(shí),拖拽功能是一項(xiàng)常見需求,為了方便實(shí)現(xiàn)拖拽功能,我們可以借助第三方庫react-beautiful-dnd,本文將介紹react-beautiful-dnd的基本概念,并結(jié)合實(shí)際的項(xiàng)目代碼一步步詳細(xì)介紹其使用過程,需要的朋友可以參考下2023-11-11
React中使用react-player 播放視頻或直播的方法
這篇文章主要介紹了React中使用react-player 播放視頻或直播,本文教大家如何使用react框架及創(chuàng)建實(shí)例的代碼,本文內(nèi)容簡短給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-01-01

