React新文檔:不要濫用Ref哦~
作者:卡頌
簡(jiǎn)介:《React技術(shù)揭秘》作者
來源:SegmentFault 思否社區(qū)
大家好,我卡頌。
React新文檔有個(gè)很有意思的細(xì)節(jié):useRef、useEffect這兩個(gè)API的介紹,在文檔中所在的章節(jié)叫Escape Hatches(逃生艙)。
顯然,正常航行時(shí)是不需要逃生艙的,只有在遇到危險(xiǎn)時(shí)會(huì)用到。
如果開發(fā)者過多依賴這兩個(gè)API,可能是誤用。
在React新文檔:不要濫用effect哦中我們談到useEffect的正確使用場(chǎng)景。
今天,我們來聊聊Ref的使用場(chǎng)景。
為什么是逃生艙?
先思考一個(gè)問題:為什么ref、effect被歸類到逃生艙中?
這是因?yàn)槎卟僮鞯亩际?strong>脫離React控制的因素。
effect中處理的是副作用。比如:在useEffect中修改了document.title。
document.title不屬于React中的狀態(tài),React無法感知他的變化,所以被歸類到effect中。
同樣,使DOM聚焦需要調(diào)用element.focus(),直接執(zhí)行DOM API也是不受React控制的。
雖然他們是脫離React控制的因素,但為了保證應(yīng)用的健壯,React也要盡可能防止他們失控。
失控的Ref
對(duì)于Ref,什么叫失控呢?
首先來看不失控的情況:
執(zhí)行ref.current的focus、blur等方法
執(zhí)行ref.current.scrollIntoView使element滾動(dòng)到視野內(nèi)
執(zhí)行ref.current.getBoundingClientRect測(cè)量DOM尺寸
執(zhí)行ref.current.remove移除DOM
執(zhí)行ref.current.appendChild插入子節(jié)點(diǎn)
export default function Counter() { const [show, setShow] = useState(true); const ref = useRef(null); return ( <div>
<button
onClick={() => {
setShow(!show);
}}>
Toggle with setState </button>
<button
onClick={() => {
ref.current.remove();
}}>
Remove from the DOM </button>
{show && <p ref={ref}>Hello world</p>} </div>
);
}

如何限制失控
高階組件
低階組件
function MyInput(props) { return <input {...props} />;
}
function MyInput(props) { const ref = useRef(null); return <input ref={ref} {...props} />;
}
function Form() { return ( <>
<MyInput/>
</>
)
}
function MyInput(props) { return <input {...props} />;
}function Form() { const inputRef = useRef(null); function handleClick() {
inputRef.current.focus();
} return ( <>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
input聚焦 </button>
</>
);
}點(diǎn)擊后,會(huì)報(bào)錯(cuò):

人為取消限制
const MyInput = forwardRef((props, ref) => { return <input {...props} ref={ref} />;
});function Form() { const inputRef = useRef(null); function handleClick() {
inputRef.current.focus();
} return ( <>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input </button>
</>
);
}
useImperativeHandle
const MyInput = forwardRef((props, ref) => { const realInputRef = useRef(null); useImperativeHandle(ref, () => ({ focus() {
realInputRef.current.focus();
},
})); return <input {...props} ref={realInputRef} />;
});
{ focus() {
realInputRef.current.focus();
},
}
總結(jié)

