0

I have a problem around React (Next) Context/Reducer. I've already written components with Context and Reducer and don't understand why it's not working as excpected.

The problem is that the reducer's state is set and logged correctly in useEffect but after a key press which calls a context function, the state seems to be a older copy.

pages/svgTest:

export default function SvgTest() {
return (
    <>
        <EditorContextProvider>
            <SVGEditor />
        </EditorContextProvider>
    </>
)

}

components/svgTest/SVGEditor:

export default function SVGEditor() {

const {brightness} = useLocalStorage()

const {itemUp, right} = useEditorContext()

useEffect(() => {

    function handleKeyDown(event: KeyboardEvent) {
        switch (event.key) {
            case 'ArrowUp':
                itemUp()
                break
            case 'ArrowRight':
                right()
                break
        }
    }

    document.addEventListener('keydown', handleKeyDown)

    return () => {
        document.removeEventListener('keydown', handleKeyDown)
    }

}, [])

return (
    <div style={{
        display: 'grid', gridTemplateColumns: '2fr 5fr',
        background: globals.brightness[brightness].background, height: 500,
    }}>
    </div>
)

}

components/svgTest/context:

const focuses = {
    tree: 'tree',
    form: 'form',
    input: 'input',
}

type EditorAction = {
    name: string,
    payload?: any
}

type EditorState = {
    focus: string
}

const initialTreeState: EditorState = {
    focus: focuses.tree,
}

export function reducer(state: EditorState, action: EditorAction): EditorState {
    const {name, payload} = action
    switch (name) {
        case 'focus': {
            return {...state, focus: payload}
        }
    }
    return state
}

export type EditorContext = {
    editorState: EditorState
    itemUp: () => void
    right: () => void
}

const EditorContext = createContext<EditorContext>({} as EditorContext)

export function EditorContextProvider(props: { children: ReactNode }) {

    const [editorState, dispatch] = useReducer(reducer, initialTreeState)

    useEffect(() => {
        console.log("logsntr", "editorState.effect", editorState)
    }, [editorState])

    function itemUp() {
        console.log("logsntr", "editorState.up", editorState)
    }

    function right() {
        dispatch({name: 'focus', payload: focuses.form})
    }

    return (
        <EditorContext.Provider value={{
            editorState, itemUp, right
        }}>
            {props.children}
        </EditorContext.Provider>
    )
}

export function useEditorContext() {
    return useContext(EditorContext)
}

The output is here after page reload:
tree
tree

After pressing up:
tree

After pressing right:
form

After pressing up:
tree

useState also behaves strangely. A solution that worked was with useRef und I supposed that an unbound state does not reflect in functions but that shouldn't be the case with the reducer, right? Could it be something with Next?

Edit:
I also tried useCallback but with the same result:

const right = useCallback(() => {
    dispatch({name: 'focus', payload: focuses.form})
}, [editorState])
sntrcode
  • 202
  • 1
  • 7
  • 10

1 Answers1

0

So I guess I was right with my suspect: updating state (useState or reducer) does not immediately reflect changes but only after re-render. And re-render apparently only happens when state is bound to Nodes.

Which means that the only option for unbound state to be reflected without re-render is using useRef.

Hint in that direction: https://react.dev/reference/react/useReducer#avoiding-recreating-the-initial-state

sntrcode
  • 202
  • 1
  • 7
  • 10