React新文檔:不要濫用Ref哦~
作者:卡頌
簡介:《React技術揭秘》作者
來源:SegmentFault 思否社區(qū)
大家好,我卡頌。
React新文檔有個很有意思的細節(jié):useRef、useEffect這兩個API的介紹,在文檔中所在的章節(jié)叫Escape Hatches(逃生艙)。
顯然,正常航行時是不需要逃生艙的,只有在遇到危險時會用到。
如果開發(fā)者過多依賴這兩個API,可能是誤用。
在React新文檔:不要濫用effect哦中我們談到useEffect的正確使用場景。
今天,我們來聊聊Ref的使用場景。
為什么是逃生艙?
先思考一個問題:為什么ref、effect被歸類到逃生艙中?
這是因為二者操作的都是脫離React控制的因素。
effect中處理的是副作用。比如:在useEffect中修改了document.title。
document.title不屬于React中的狀態(tài),React無法感知他的變化,所以被歸類到effect中。
同樣,使DOM聚焦需要調(diào)用element.focus(),直接執(zhí)行DOM API也是不受React控制的。
雖然他們是脫離React控制的因素,但為了保證應用的健壯,React也要盡可能防止他們失控。
失控的Ref
對于Ref,什么叫失控呢?
首先來看不失控的情況:
執(zhí)行ref.current的focus、blur等方法
執(zhí)行ref.current.scrollIntoView使element滾動到視野內(nèi)
執(zhí)行ref.current.getBoundingClientRect測量DOM尺寸
執(zhí)行ref.current.remove移除DOM
執(zhí)行ref.current.appendChild插入子節(jié)點
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>
</>
);
}點擊后,會報錯:

人為取消限制
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();
},
}
總結

