React async state

Viewed 80

I have a problem with state synchronization. When I click on the outside of the editor (wanna close it) I want to pass the actual text back to parent node (callback function). But when I click outside the queryText state seems to be always one step behind. (E.g.: If there is abc inside the Editor, I type d, CALLBACK: abc, I type e, CALLBACK: abcd and so on...).

How can I achieve that if I click outside of my Editor there will be the actual state of queryText?

import React, {useEffect, useState} from "react";
import AceEditor from "react-ace";

import "ace-builds/src-noconflict/mode-mysql";
import "ace-builds/src-noconflict/theme-eclipse";
import "./SQLEditor.css"


function SQLEditor({queryTab, active, callback}) {
    const [queryText, setQueryText] = useState(queryTab[active].content)

    //Setting a onClickListener somewhere else
    //This function is getting called when I click outside of the Editor
    function handleClickOutside() {
            document.removeEventListener("mousedown", handleClickOutside);
            console.log("CALLBACK:" + queryText) //Problem here
            callback(active, {content: queryText})
            setInEditor(false)
        }
    }

    //Implementing useEffect for debugging

    useEffect(() => {
        console.log(queryText); //Here I'm getting the right one.
    }
    return (
            <AceEditor
                mode="mysql"
                theme="eclipse"
                name="blah2"
                onChange={(newValue) => {
                    setQueryText(newValue) //Seting text to new value
                    console.log(newValue) //Getting the correct updated value
                }}
                fontSize={16}
            />
        </div>
    )
}
export default SQLEditor;
2 Answers

I've haven't seen useEffect return a whole component before and I can't get your code to run in a sandbox. However, I had the same issue the other day getting my state to be synchronous. Here's how I handled it

  1. Create useState hook that gets set on click
  2. Add the state to your useEffect dependency list
  3. Move on click actions to your useEffect method

It could look something like this

const [someVar, setSomeVar] = useState(null);

const handleClick = (e) => {
    setSomeVar(e.target.value); // or whatever
}

useEffect(() => {
    if (someVar != null) { // useEffect will get called on mount, so this logic is to ensure it will only get called if someVar is not equal to it's default value
        // implement handle click logic here
    }
}, [someVar]) // useEffect will get called each time someVar gets updated because we've added someVar to useEffect's dependencies

Add below useEffect,

useEffect(() => {
    if(!inEditor){
        callback(active, {content: queryText});
        console.log(queryText);
    }
}, [inEditor, queryText])

And update your handleClickOutside to,

function handleClickOutside() {
    document.removeEventListener("mousedown", handleClickOutside);
    setInEditor(false);
}
Related