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
- setFocusStateto actually modify the value of- focusStatewhen called from within the- Pointcomponent
- For that to result in an actual modification of the cursor position via setCursor
 
                        
First of all: if you log
focusStateright after setting it, you won't even log the new value because:the
Pointcomponent is rendered withfocusState' old value (let's call it the #1 value)you add an effect that calls
setCursor, the effect has empty array dependencies it gets called but it won't get called againyou set
focusState(to a #2 value) into the handler. Contextually, you log it hoping it contains the new value but...since you set
focusStatethe component re-renders to render with the newfocusStatevalue (#2)your effect doesn't call
setCursorbecause it doesn't depend onfocusStateI think that this is your problem
Did you add the
// eslint-disable-next-linefor the sake of asking this question or because you'd like to avoid callingsetCursorevery time the reference tosetCursoris new (at every render)? If the latter, please consider refactoring it toand removing the
setCursorfunction at all.Please note: this answer could be incomplete, please update the question with a playable CodeSandbox to fix definitely your problem.