skip to Main Content

I have component that have state as object

const [data, setData] = useState({
  input, 
  output: '', 
  enableCopyToClipboard: true,
}

When listen to Observable I do following:

  // reset state before loading stream
  setData({ 
    ...data, 
    output: '',
    enableCopyToClipboard: false,
  });

  loadingStream(request).subscribe((response) => {
    resText.push(response.text);
    setData({ 
      ...data, 
      output: resText.join(''), 
    });
  });

Problem is my enableCopyToClipboard variable in state stays true when it should be false while streaming is ongoing. No idea why.

2

Answers


  1. Multiple state updates in the same render, as well as state updates within a closure, can cause this problem. An often recommended approach is to use the callback version of state updates. For example:

    setData(d => ({ 
      ...d, 
      output: '',
      enableCopyToClipboard: false,
    }});
    
    loadingStream(request).subscribe((response) => {
      resText.push(response.text);
      setData(d => ({ 
        ...d, 
        output: resText.join(''), 
      }));
    });
    

    In the case of state updates within a closure, the difference here is that the data variable isn’t being captured by that closure and there’s no dependency on it. In the case of batched state updates, the difference here is that the callback uses the current state in the batch sequence rather than the state at the time of rendering.

    Login or Signup to reply.
  2. You can use the callback function to set the new data. So you’ll always have the most up to date version of the state. This prevents the override of the old version of the state.

    // reset state before loading stream
    setData((prevData) => ({
      ...data,
      output: "",
      enableCopyToClipboard: false,
    }));
    
    loadingStream(request).subscribe((response) => {
      resText.push(response.text);
      setData((prevData) => ({
        ...prevData,
        output: resText.join(""),
      }));
    });
    

    You could also set the enableCopyToClipboard to false by default on the subscribe.

    loadingStream(request).subscribe((response) => {
      resText.push(response.text);
      setData((prevData) => ({
        ...prevData,
        enableCopyToClipboard: false,
        output: resText.join(""),
      }));
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search