Reputation: 121
The problem is that useRef is triggered during first render. Two examples when it could be a problem.
- When one can have some loading
const Problem1 = () => {
const ref = useRef();
if (loading)
return null;
return <input ref={ref} value={} />;
}
- When ref is inside some condition.
const Problem2 = () => {
const ref = useRef();
return user ? <input ref={ref} value={user.name} /> : <Exit >;
}
sandbox example https://codesandbox.io/s/nifty-feynman-z68k0
In the second case I at least could show element at the beginning with display: none. But how to solve first problem I have no idea.
What are the best practices in these cases?
Upvotes: 4
Views: 13951
Reputation: 647
When your conditional element gets assigned to ref.current, it doesn't cause a rerender. You can use a useCallback
instead, which will run a callback when the element gets rendered:
const Problem2 = () => {
const ref = useCallback(node = () => {
...callback code here
});
// Code here won't rerun for a change in ref.current
return user ? <input ref={ref} value={user.name} /> : <Exit >;
}
If you want the whole code of Problem2 to run again (now with a reference to ref), you can add a useState:
const Problem2 = () => {
const [myInput, setMyInput] = useState(null)
const ref = useCallback(node => {
setMyInput(node)
});
// Now any code here will run twice, once for myInput
// equals null, and once when it points to the element
return user ? <input ref={ref} value={user.name} /> : <Exit >;
}
Upvotes: 1
Reputation: 31335
See if this works for you:
From React DOCS: https://reactjs.org/docs/hooks-reference.html#useref
useRef()
However, useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.
This works because useRef() creates a plain JavaScript object. The only difference between useRef() and creating a {current: ...} object yourself is that useRef will give you the same ref object on every render.
The useRef
object won't change across renders. You'll always have the same reference for the useRef
object with a current
property. What may change is what you are keeping inside that current
property.
function App() {
const input1_ref = React.useRef(null);
const input2_ref = React.useRef(null);
const [showInput2, setShowInput2] = React.useState(false);
React.useEffect(()=>{
input1_ref.current ?
console.log('Input1 has been mounted...')
: console.log('Input1 has NOT been mounted...');
input2_ref.current ?
console.log('Input2 has been mounted...')
: console.log('Input2 has NOT been mounted...');
});
return(
<React.Fragment>
<div>Input 1</div>
<input type='text' ref={input1_ref}/>
{showInput2 &&
<React.Fragment>
<div>Input 2</div>
<input type='text' ref={input2_ref}/>
</React.Fragment>
}
<div>
<button onClick={()=>setShowInput2((prevState)=>!prevState)}>Click</button>
</div>
</React.Fragment>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
Upvotes: 5