When to use useImperativeHandle, useLayoutEffect, and useDebugValue
我不明白为什么需要以下
让我通过说所有这些钩子很少使用来开头这个答案。 99%的时间,您将不需要这些。它们仅旨在涵盖一些罕见的极端情况。
通常,当您使用
您可能要执行上述任何一项操作,可能有很多原因;您可能不想将本机属性公开给父级,或者您想更改本机函数的行为。可能有很多原因。但是,很少使用
useImperativeHandle customizes the instance value that is exposed to parent components when usingref
示例
在此示例中,我们从
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | const MyInput = React.forwardRef((props, ref) => { const [val, setVal] = React.useState(''); const inputRef = React.useRef(); React.useImperativeHandle(ref, () => ({ blur: () => { document.title = val; inputRef.current.blur(); } })); return ( <input ref={inputRef} val={val} onChange={e => setVal(e.target.value)} {...props} /> ); }); const App = () => { const ref = React.useRef(null); const onBlur = () => { console.log(ref.current); // Only contains one property! ref.current.blur(); }; return <MyInput ref={ref} onBlur={onBlur} />; }; ReactDOM.render(<App />, document.getElementById("app")); |
1 2 | <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"> |
虽然在某种程度上类似于
The signature is identical to
useEffect , but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled insideuseLayoutEffect will be flushed synchronously, before the browser has a chance to paint.
示例
假设您有一个绝对定位的元素,其高度可能会有所不同,并且您想在其下放置另一个
在这里您要使用
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | const Message = ({boxRef, children}) => { const msgRef = React.useRef(null); React.useEffect(() => { const rect = boxRef.current.getBoundingClientRect(); msgRef.current.style.top = `${rect.height + rect.top}px`; }, []); return <span ref={msgRef} className="msg">{children}</span>; }; const App = () => { const [show, setShow] = React.useState(false); const boxRef = React.useRef(null); return ( setShow(prev => !prev)}>Click me {show && <Message boxRef={boxRef}>Foo bar baz</Message>} ); }; ReactDOM.render(<App />, document.getElementById("app")); |
1 2 3 4 5 6 7 8 9 10 11 12 | .box { position: absolute; width: 100px; height: 100px; background: green; color: white; } .msg { position: relative; border: 1px solid red; } |
1 2 | <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"> |
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | const Message = ({boxRef, children}) => { const msgRef = React.useRef(null); React.useLayoutEffect(() => { const rect = boxRef.current.getBoundingClientRect(); msgRef.current.style.top = `${rect.height + rect.top}px`; }, []); return <span ref={msgRef} className="msg">{children}</span>; }; const App = () => { const [show, setShow] = React.useState(false); const boxRef = React.useRef(null); return ( setShow(prev => !prev)}>Click me {show && <Message boxRef={boxRef}>Foo bar baz</Message>} ); }; ReactDOM.render(<App />, document.getElementById("app")); |
1 2 3 4 5 6 7 8 9 10 11 12 | .box { position: absolute; width: 100px; height: 100px; background: green; color: white; } .msg { position: relative; border: 1px solid red; } |
1 2 | <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"> |
有时您可能想调试某些值或属性,但是这样做可能需要昂贵的操作,这可能会影响性能。
useDebugValue can be used to display a label for custom hooks in React DevTools.
我个人从未使用过此钩子。也许评论中的某人可以通过一个很好的例子来提供一些见解。
[index.tsx]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import React, { useRef } from"react"; import { render } from"react-dom"; import Button from"./Button"; import"./styles.css"; function App() { const buttonRef = useRef(null); const handleClick = () => { console.log(Object.keys(buttonRef.current)); // ['someExposedProperty'] console.log("click in index.tsx"); buttonRef.current.someExposedProperty(); }; return ( <Button onClick={handleClick} ref={buttonRef} /> ); } const rootElement = document.getElementById("root"); render(<App />, rootElement); |
[Button.tsx]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import React, { useRef, useImperativeHandle, forwardRef } from"react"; function Button(props, ref) { const buttonRef = useRef(); useImperativeHandle(ref, () => ({ someExposedProperty: () => { console.log(`we're inside the exposed property function!`); } })); return ( <button ref={buttonRef} {...props}> Button </button> ); } export default forwardRef(Button); |
在这里可用。
与
99% of the time [
useEffect ] is what you want to use.
我还没有看到任何示例可以很好地说明这一点,而且我不确定我也可以创建任何东西。最好说当
我觉得文档是一个很好的例子来说明这一点。如果您有一个自定义钩子,并且希望在React DevTools中对其进行标记,那么您将使用它。
如果您对此有任何特定的问题,那么最好是发表评论或提出其他问题,因为我觉得人们在这里所做的任何事情都只会重申文档,至少直到我们遇到一个更具体的问题为止。
useImperativeHandle
通常通过将功能组件置于forwardRef内,从而将基于功能的组件方法和属性公开给其他组件
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | const Sidebar=forwardRef((props,ref)=>{ const [visibility,setVisibility]=useState(null) const opensideBar=()=>{ setVisibility(!visibility) } useImperativeHandle(ref,()=>({ opensideBar:()=>{ set() } })) return( <Fragment> <button onClick={opensideBar}>SHOW_SIDEBAR</button> {visibility==true?( <ul className="list-group"> <li className=" list-group-item">HOME </li> <li className=" list-group-item">ABOUT </li> <li className=" list-group-item">SERVICES </li> <li className=" list-group-item">CONTACT </li> <li className=" list-group-item">GALLERY </li> </ul> </aside> ):null} </Fragment> ) } //using sidebar component |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Main extends Component{ myRef=createRef(); render(){ return( <Fragment> <button onClick={()=>{ ///hear we calling sidebar component this.myRef?.current?.opensideBar() }}> Show Sidebar </button> <Sidebar ref={this.myRef}/> </Fragment> ) } } |