简体   繁体   中英

How do React refs behave inside useCallback?

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;

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM