0

I understand (somewhat) how to use useEffect to update state, but I struggle with situations like when you need current state inside of another function, before the "nextTick" as it were.

Here is a simple Codepen with the exact issue. Make sure the Pen console is open.

https://codepen.io/kirkbross/pen/vYRNpqG?editors=1111

const App = () => {
  const [state, setState] = React.useState(null);

  // how can I make sure the below function knows what the current state really is?
  const handleAppend = (state) => {
    console.log("click");
    console.log(state?.text + " foobar");
  };

  return (
    <div class="app">
      <div className="row">
        <span>Text: </span>
        <input
          type="text"
          onChange={() => setState({ text: e.target.value })}
        />
      </div>
      <div className="row">
        <button onClick={handleAppend}>
          Append "foobar" to text and log it to console
        </button>
      </div>
    </div>
  );
};
Kirk Ross
  • 6,413
  • 13
  • 61
  • 104

3 Answers3

0

You're shadowing your state variable in your handleAppend function. You don't need to pass in an argument since state is available in scope of the component

  const handleAppend = () => {
    console.log("click");
    console.log(state?.text + " foobar");
  };
larz
  • 5,724
  • 2
  • 11
  • 20
  • Thank you. My actual app is much more complex so I think I over simplified my pen. The actual function I need to have "instant state" in is a useCallback for `react-beautiful-dnd` onDragEnd function. However, you mention the term "shadowing state" which I think is probably the knowledge gat at the core of my problem. I'll dig into the docs and learn how to avoid shadowing state. – Kirk Ross Jul 05 '22 at 20:19
0

I did some changes. You dont need to use ur state as a parameter, since your textState lives inside your app component and there for you can reach it within your function.

Also, i changed the state and setState to textState, setTextState to make it less confusing. Also after clicking on the button and console logging, i cleared the textState so the next value wont be effected. Check it out below.

function App() {
  const [textState, setTextState] = React.useState(null);

  const handleAppend = () => {
    console.log("click");
    console.log(textState +  " foobar");
    setTextState('')
    //also, you could make the input box clear after each press on button by adding value={textState} in the input.
  };

  return (
    <div className="App">
      <input
          type="text"
          onChange={(e) => setTextState(e.target.value)}
        />

<button onClick={handleAppend}>
          Append "foobar" to text and log it to console
        </button>

    </div>
  );
}
Freddy
  • 48
  • 1
  • 7
  • Thank you. My actual app is much more complex so I think I over simplified my pen. The actual function I need to have "instant state" in is a useCallback for react-beautiful-dnd onDragEnd function. Larz below mentions 'shadowing state' which I think is my problem. I'll dig into the docs. – Kirk Ross Jul 05 '22 at 20:20
0

My real world case was more complicated than the Pen. The actual function needing state was a useCallback function and I had forgotten to add state to the dep array of the useCallback function.

const handleDragEnd = useCallback(
  async (result) => {
    const { source, destination, draggableId } = result;
    console.log(state); // shows up now.
  },
  [state], // I had forgotten to add state to the useCallback dep array
);

Kirk Ross
  • 6,413
  • 13
  • 61
  • 104