Reactjs 如何在反应状态更新/渲染后设置焦点
在React中使用功能组件和挂钩,我很难将焦点转移到新添加的元素。看到这一点的最短途径可能是以下组件: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的 //通过这种方式,键
功能待办事项(道具){
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