After I set the state from the loadable within the App.js file:
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;
Calling the setFocusState
function from within the Point
component doesn't seem to work:
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>
)
}
When I add console.log(focusState)
to the relevant parts of the switch statement within function handleKeyEvent
, it shows that every time setFocusState
is called from within the Point component, nothing changes and the value of focusState
remains the same value as the initial setting. I am guessing this is why setCursor
doesn't get called via useEffect.
Would anyone be able to advise what is going wrong here? What would need to be changed for
setFocusState
to actually modify the value offocusState
when called from within thePoint
component- For that to result in an actual modification of the cursor position via
setCursor