title: setState second argument callback function alternative in state hooks
body: <p>I made a code sandbox example for my problem: <a href="https://codesandbox.io/s/react-form-submit-problem-qn0de" rel="nofollow noreferrer">https://codesandbox.io/s/react-form-submit-problem-qn0de</a>. Please try to click the "+"/"-" button on both Function Example and Class Example and you'll see the difference. On the Function Example, we always get the previous value while submitting.</p>
  
  <p>I'll explain details about this example below.</p>
  
  <p>We have a react component like this</p>
  
  <pre><code>function Counter(props) {
    return (
      &lt;&gt;
        &lt;button type="button" onClick={() =&gt; props.onChange(props.value - 1)}&gt;
          -
        &lt;/button&gt;
        {props.value}
        &lt;button type="button" onClick={() =&gt; props.onChange(props.value + 1)}&gt;
          +
        &lt;/button&gt;
        &lt;input type="hidden" name={props.name} value={props.value} /&gt;
      &lt;/&gt;
    );
  }
  </code></pre>
  
  <p>It contains two buttons and a numeric value. User can press the '+' and '-' button to change the number. It also renders an input element so we can use it in a <code>&lt;form&gt;</code>.</p>
  
  <p>This is how we use it</p>
  
  <pre><code>class ClassExample extends React.Component {
    constructor(props) {
      super(props);
  
      this.state = {
        value: 1,
        lastSubmittedQueryString: ""
      };
  
      this.formEl = React.createRef();
    }
  
    handleSumit = () =&gt; {
      if (this.formEl.current) {
        const formData = new FormData(this.formEl.current);
        const search = new URLSearchParams(formData);
        const queryString = search.toString();
        this.setState({
          lastSubmittedQueryString: queryString
        });
      }
    };
  
    render() {
      return (
        &lt;div className="App"&gt;
          &lt;h1&gt;Class Example&lt;/h1&gt;
          &lt;form
            onSubmit={event =&gt; {
              event.preventDefault();
              this.handleSumit();
            }}
            ref={ref =&gt; {
              this.formEl.current = ref;
            }}
          &gt;
            &lt;Counter
              name="test"
              value={this.state.value}
              onChange={newValue =&gt; {
                this.setState({ value: newValue }, () =&gt; {
                  this.handleSumit();
                });
              }}
            /&gt;
            &lt;button type="submit"&gt;submit&lt;/button&gt;
            &lt;br /&gt;
            lastSubmittedQueryString: {this.state.lastSubmittedQueryString}
          &lt;/form&gt;
        &lt;/div&gt;
      );
    }
  }
  </code></pre>
  
  <p>We render our <code>&lt;Counter&gt;</code> component in a <code>&lt;form&gt;</code>, and <strong>want to submit this form right after we change the value of <code>&lt;Counter&gt;</code></strong>. However, on the onChange event, if we just do</p>
  
  <pre><code>onChange={newValue =&gt; {
    this.setState({ value: newValue });
    this.handleSubmit();
  }}
  </code></pre>
  
  <p>then we won't get the updated value, probably because React doesn't run setState synchronously. So instead we put <code>this.handleSubmit()</code> in the second argument callback of setState to make sure it is executed after the state has been updated.</p>
  
  <p>But in the Function Example, as far as I know in state hooks there's nothing like the second argument callback function of setState. So we cannot achieve the same goal. We found out two workarounds but we are not satisfied with either of them.</p>
  
  <p><strong>Workaround 1</strong></p>
  
  <p>We tried to use the effect hook to listen when the value has been changed, we submit our form.</p>
  
  <pre><code>React.useEffect(() =&gt; {
    handleSubmit();
  }, [value])
  </code></pre>
  
  <p>But sometimes we need to just change the value without submitting the form, we want to invoke the submit event <strong>only when we change the value by clicking the button</strong>, so we think it should be put in the button's onChange event.</p>
  
  <p><strong>Workaround 2</strong></p>
  
  <pre><code>onChange={newValue =&gt; {
    setValue(newValue);
    setTimeout(() =&gt; {
      handleSubmit();
    })
  }}
  </code></pre>
  
  <p>This works fine. We can always get the updated value. But the problem is we don't understand how and why it works, and we never see people write code in this way. We are afraid if the code would be broken with the future React updates.</p>
  
  <p>Sorry for the looooooooong post and thanks for reading my story. Here are my questions:</p>
  
  <ol>
  <li>How about Workaround 1 and 2? Is there any 'best solution' for the Function Example?</li>
  <li>Is there anything we are doing wrong? For example maybe we shouldn't use the hidden input for form submitting at all?</li>
  </ol>
  
  <p>Any idea will be appreciated :)</p>