Reputation: 799
I wouldn't expect the following React app to work properly, but it does. I'd expect the useCallback hook to capture and preserve the initial value of the ref. I understand that the ref couldn't be listed in the dependency array so maybe this is a special case only intended for refs?
Why isn't the content of newTodoRef.current.value captured by useCallback just once when App first renders?
import React, { useCallback, useReducer, useRef } from 'react';
type Todo = { id: number, text: string }
type ActionType = { type: 'ADD', text: string} | { type: 'REMOVE', id: number}
const todoReducer = (state: Todo[], action: ActionType) => {
switch(action.type) {
case 'ADD': return [ ...state, { id: state.length, text: action.text }]
case 'REMOVE': return state.filter(({ id }) => id !== action.id) // this is buggy, but that's beside the point
default: throw new Error()
}
}
function App() {
const [todos, dispatch] = useReducer(todoReducer, [])
const newTodoRef = useRef<HTMLInputElement>(null)
const onAddTodo = useCallback(() => {
if (newTodoRef.current) {
dispatch({ type: "ADD", text: newTodoRef.current.value })
newTodoRef.current.value = ''
}
}, [])
return (
<div>
{todos.map(todo => (
<div key={todo.id}>
{todo.text}
<button onClick={() => dispatch({ type:"REMOVE", id: todo.id })}>Remove</button>
</div>
))}
<input type="text" ref={newTodoRef}/>
<button onClick={onAddTodo}>ADD</button>
</div>
)
}
export default App;
Upvotes: 3
Views: 3124
Reputation: 85022
Why isn't the content of newTodoRef.current.value captured by useCallback just once when App first renders?
The reference to the top level object, newTodoRef
, is captured in this way. The reason this works out fine is that the ref is a mutable object, and the same object on every render. Once react has created the div element on the page, it will mutate newTodoRef
, changing its .current
property to the element. Then later, you access newTodoRef
, which is still the same object, and you get it's .current
property. The property has changed in the meantime, but the object has not.
Upvotes: 4