Reactjs 如何在反应状态更新/渲染后设置焦点

Reactjs 如何在反应状态更新/渲染后设置焦点,reactjs,Reactjs,在React中使用功能组件和挂钩,我很难将焦点转移到新添加的元素。看到这一点的最短途径可能是以下组件: 功能待办事项(道具){ const addButton=React.useRef(null) const[todos,setTodos]=React.useState(不可变的.List([])) const addTodo=e=>{ setTodos(todos.push('Todo text…')) //在添加TODO之后,我想在这里 //将焦点转移到包含新TODO的 //通过这种方式,键

在React中使用功能组件和挂钩,我很难将焦点转移到新添加的元素。看到这一点的最短途径可能是以下组件:

功能待办事项(道具){
const addButton=React.useRef(null)
const[todos,setTodos]=React.useState(不可变的.List([]))
const addTodo=e=>{
setTodos(todos.push('Todo text…'))
//在添加TODO之后,我想在这里
//将焦点转移到包含新TODO的
  • //通过这种方式,键盘用户可以选择使用什么 //新增的待办事项 } const updateTodo=(索引,值)=>{ setTodos(todos.set(索引,值)) } 常量removeTodo=索引=>{ setTodos(todos.delete(索引)) addButton.current.focus() } 返回 添加待办事项
      {todo.map((todo,index)=>(
    • updateTodo(索引,e.target.value)}/>
    • ))}
    } ReactDOM.render(React.createElement(Todos,{}),document.getElementById('app'))
  • 仅供参考,
    todos.map
    将真实地呈现一个
    Todo
    组件,该组件具有被选择、使用键盘上下移动等功能。这就是为什么我试图聚焦
  • ,而不是其中的输入(我意识到可以使用
    自动聚焦
    属性来完成)

    理想情况下,我可以调用
    setTodos
    ,然后立即对新的todo调用
    .focus()
    ,但这是不可能的,因为新的todo在DOM中还不存在,因为渲染尚未发生


    我想我可以通过状态跟踪焦点来解决这个问题,但这需要捕获
    onFocus
    onBlur
    并保持状态变量最新。这似乎有风险,因为焦点可以通过键盘、鼠标、点击、开关、操纵杆等进行剧烈移动……窗口可能会失去焦点…

    只需简单地包装
    focus()
    setTimeout中调用

    setTimeout(() => {
      addButton.current.focus()
    })
    

    使用订阅
    todos
    更新的
    useffect
    ,并在更新后设置焦点

    例如:

    useEffect(() => {
     addButton.current.focus()
    }, [todos])
    
    最新答复:

    因此,您在按钮上只有一个ref。这不允许您访问todo本身来聚焦它,只允许使用
    addButton
    。我添加了一个
    currentTodo
    ref,默认情况下它将分配给最后一个todo。这只是用于拥有一个todo并聚焦最近添加的todo的默认渲染。您需要如果您只想删除输入,您可以找到一种方法来集中输入

    ref={index==todos.length-1?currentTodo:null}
    将把ref分配给索引中的最后一项,否则ref为null

    import React, { useState, useEffect } from 'react';
    import ReactDOM from 'react-dom';
    
    function Todos(props) {
        const currentTodo = React.useRef(null)
        const addButton = React.useRef(null)
        const [todos, setTodos] = useState([])
    
        useEffect(() => {
            const newTodos = [...todos];
            newTodos.push('Todo text...');
            setTodos(newTodos);
    
            // event listener for click
            document.addEventListener('mousedown', handleClick);
    
            // removal of event listener on unmount
            return () => {
                document.removeEventListener('mousedown', handleClick);
            };
        }, []);
    
    
        const handleClick = event => {
            // if there's a currentTodo and a current addButton ref
            if(currentTodo.current && addButton.current){
                // if the event target was the addButton ref (they clicked addTodo)
                if(event.target === addButton.current) {
                    // select the last todo (presumably the latest)
                    currentTodo.current.querySelector('input').select();
                }
            }
        }
    
        const addTodo = e => {
            const newTodo = [...todos];
            newTodo.push('New text...');
            setTodos(newTodo);
        }
    
        // this is for if you wanted to focus the last on every state change
        // useEffect(() => {
        //     // if the currentTodo ref is set
        //     if(currentTodo.current) {
        //         console.log('input', currentTodo.current.querySelector('input'));
        //         currentTodo.current.querySelector('input').select();
        //     }
        // }, [todos])
    
        const updateTodo = (index, value) => {
            setTodos(todos.set(index, value))
        }
    
        const removeTodo = index => {
            setTodos(todos.delete(index))
            currentTodo.current.focus()
        }
    
        return <div>
            <button onClick={addTodo} ref={addButton}>Add todo</button>
            <ul>
                {todos.length > 0 && todos.map((todo, index) => (
                    <li tabIndex="0" aria-label={`Todo ${index + 1} of ${todos.length}`} key={index} ref={index === todos.length -1 ? currentTodo : null}>
                        <input type="text" value={todo} onChange={e => updateTodo(index, e.target.value)} />
                        <a onClick={e => removeTodo(index)} href="#">Delete todo</a>
                    </li>
                ))}
            </ul>
        </div>
    }
    
    ReactDOM.render(React.createElement(Todos, {}), document.getElementById('root'))
    
    import React,{useState,useffect}来自“React”;
    从“react dom”导入react dom;
    功能待办事项(道具){
    const currentTodo=React.useRef(null)
    const addButton=React.useRef(null)
    const[todos,setTodos]=useState([]
    useffect(()=>{
    常数newTodos=[…todos];
    newTodos.push('Todo text…');
    setTodos(newTodos);
    //单击的事件侦听器
    文件。添加的列表器(“鼠标向下”,把手点击);
    //卸载时删除事件侦听器
    return()=>{
    document.removeEventListener('mousedown',handleClick);
    };
    }, []);
    const handleClick=事件=>{
    //如果有currentTodo和CurrentAddButton ref
    if(currentTodo.current&&addButton.current){
    //如果事件目标是addButton引用(他们单击addTodo)
    if(event.target==addButton.current){
    //选择最后一个todo(可能是最新的)
    currentTodo.current.querySelector('input').select();
    }
    }
    }
    const addTodo=e=>{
    常数newTodo=[…todos];
    newTodo.push('newtext…');
    赛托多斯(新托多);
    }
    //这是为了如果你想把最后一个重点放在每一个状态的变化上
    //useffect(()=>{
    ////如果设置了currentTodo ref
    //如果(当前Todo.current){
    //console.log('input',currentTodo.current.querySelector('input');
    //currentTodo.current.querySelector('input').select();
    //     }
    //},[待办事项])
    const updateTodo=(索引,值)=>{
    setTodos(todos.set(索引,值))
    }
    常量removeTodo=索引=>{
    setTodos(todos.delete(索引))
    currentTodo.current.focus()
    }
    返回
    添加待办事项
    
      {todos.length>0&&todos.map((todo,index)=>(
    • updateTodo(索引,e.target.value)}/>
    • ))}
    } ReactDOM.render(React.createElement(Todos,{}),document.getElementById('root'))
    这在React中没有考虑。超时让我担心,因为取决于浏览器速度,它仍可能在DOM更新之前触发,或者触发得太晚,并从用户尝试执行的任何操作中窃取焦点。不过,这在
    addTodo
    回调中不起作用。理想情况下,我希望
    addTodo()。然后(todo=>todo.focus())
    能否将ref传递给新的todo并聚焦ref?是的。但是如何将
    todoRef.focus()
    调用延迟到渲染发生且DOM节点位于页面中之后?例如,
    addTodo();todoRefs[lastTodo].focus()
    不起作用,因为React缓冲渲染后没有立即添加ref。您应该能够使用
    useEffect
    等待TODO渲染。因此,类似于上面的内容,但不是使用
    addButton
    焦点,而是使用t