从 App.js 文件中的可加载项设置状态后:
import React from 'react';
import { useRecoilState, useSetRecoilState, useRecoilValueLoadable } from 'recoil';
import './App.css';
import { Point } from './components/Point';
import { FocusState } from './context/FocusState';
import { ItemListState } from './context/ItemListState';
import { RootState } from './context/RootState';
import { DataState } from './context/DataState';
function App() {
const setFocusState = useSetRecoilState(FocusState);
const setItemListState = useSetRecoilState(ItemListState);
const [rootState, setRootState] = useRecoilState(RootState);
const dataStateLoadable = useRecoilValueLoadable(DataState);
switch (dataStateLoadable.state) {
case 'hasValue':
let dataState = dataStateLoadable.contents;
let {root, focus, items} = dataState;
setFocusState(focus);
setItemListState(items);
setRootState(root);
return (
<div className="App">
<Point key={rootState} id={rootState} depth={0} />
</div>
)
case 'loading':
return (
<div className="App">
<p>Loading...</p>
</div>
)
case 'hasError':
throw dataStateLoadable.contents;
default:
return (
<div className="App">
<p>Loading...</p>
</div>
)
}
}
export default App;
setFocusState
从组件内调用函数Point
似乎不起作用:
export const Point: React.FC<{id: string, depth: number}> = ({id, depth}) => {
const pointRef = useRef<HTMLDivElement>(null);
const [focusState, setFocusState] = useRecoilState(FocusState);
const [itemState, setItemState] = useRecoilState(SingleItemStateFamily(id))
const parentPoint = useRecoilValue(SingleItemStateFamily(itemState.parent));
const grandparentPoint = useRecoilValue(SingleItemStateFamily(parentPoint.parent));
const setCursor = () => {
// mignt be null
const node = pointRef.current;
let range = document.createRange();
let sel = window.getSelection();
if (node !== null && node.childNodes.length > 0 && focusState.id === id) {
console.log(node, node.childNodes, focusState)
// select a range first
range.setStart(node.childNodes[0], focusState.cursorPosition);
range.setEnd(node.childNodes[0], focusState.cursorPosition);
// perform selection
sel?.removeAllRanges();
sel?.addRange(range);
node.focus();
}
}
const handleChange = (evt) => {
let newState = {...itemState};
newState.content = evt.currentTarget.innerHTML;
setItemState(newState);
}
const handleKeyEvent = (evt) => {
switch (evt.key) {
case "ArrowUp":
evt.preventDefault();
console.log("Shift focus to item above", parentPoint.children, itemState.id, parentPoint.children.indexOf(itemState.id));
// if it is the first child of a parent, shift focus to the parent
if (parentPoint.children.indexOf(itemState.id) === 0) {
console.log("Shift focus to parent")
setFocusState({id: parentPoint.id, cursorPosition: focusState.cursorPosition});
console.log(focusState);
}
// else, go to the next highest sibling
// the cursorPosition should be min(focusState.curpos, newPoint.content.length)
else {
console.log("Shift focus to previous sibling")
setFocusState({
id: parentPoint.children[parentPoint.children.indexOf(itemState.id)-1],
cursorPosition: focusState.cursorPosition
});
console.log(focusState);
}
break;
case "ArrowDown":
evt.preventDefault();
console.log("Shift focus to item below", parentPoint.children, itemState.id, parentPoint.children.indexOf(itemState.id));
// if it is the last child of a parent, shift focus to the parent's next sibling
if (parentPoint.children.indexOf(itemState.id) === parentPoint.children.length - 1) {
console.log("Shift focus to parent's next sibling")
setFocusState({
id: grandparentPoint.children[grandparentPoint.children.indexOf(parentPoint.id) + 1],
cursorPosition: focusState.cursorPosition
})
}
// else if it has any children, shift focus to the first child
else if (itemState.children.length > 0) {
console.log("Shift focus to first child")
setFocusState({
id: itemState.children[0],
cursorPosition: focusState.cursorPosition
})
}
// else, go to the next lowest sibling
else {
console.log("Shift focus to next sibling")
setFocusState({
id: parentPoint.children[parentPoint.children.indexOf(itemState.id)+1],
cursorPosition: focusState.cursorPosition
});
}
break;
case "Tab":
evt.preventDefault();
if (evt.shiftKey) {
console.log("Dedent item");
} else {
console.log("Indent item");
}
break;
default:
break;
}
}
const handleBlur = (evt) => {
let sel = window.getSelection();
let offset = sel?.anchorOffset as number;
setFocusState({
id: focusState.id,
cursorPosition: offset
})
}
useEffect(() => {
setCursor();
// eslint-disable-next-line
}, [])
return (
<ul className="point-item">
<li>
<ContentEditable onChange={handleChange}
html={itemState.content}
onKeyDown={handleKeyEvent}
innerRef={pointRef}
onBlur={handleBlur}
/>
</li>
{itemState.children.map((child_id: string) => (
<Point key={child_id} id={child_id} depth={depth+1}/>
))}
</ul>
)
}
当我console.log(focusState)
在 function 中添加 switch 语句的相关部分时handleKeyEvent
,它表明每次setFocusState
从 Point 组件中调用时,没有任何变化,并且值focusState
保持与初始设置相同。我猜这就是为什么setCursor
不通过useEffect调用的原因。
有人可以建议这里出了什么问题吗?需要改变什么
setFocusState
实际修改从组件focusState
内调用时的值Point
- 为此,通过实际修改光标位置
setCursor