Skip to content

Bug: FunctionComponent re-render phase cause a bug #20675

Closed

Description

@careyke

React version:v17.0.1

Steps To Reproduce

There maybe be a bug in following method

function updateEffectImpl(fiberFlags, hookFlags, create, deps): void {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  let destroy = undefined;

  if (currentHook !== null) {
    const prevEffect = currentHook.memoizedState;
    destroy = prevEffect.destroy;
    if (nextDeps !== null) {
      const prevDeps = prevEffect.deps;
      if (areHookInputsEqual(nextDeps, prevDeps)) {
        pushEffect(hookFlags, create, destroy, nextDeps);
        return;
      }
    }
  }

  currentlyRenderingFiber.flags |= fiberFlags;

  hook.memoizedState = pushEffect(
    HookHasEffect | hookFlags,
    create,
    destroy,
    nextDeps,
  );
}

Link to the source code: https://github.com/facebook/react/blob/e316f785526e503eceed1ae33b7a06440084b9c9/packages/react-reconciler/src/ReactFiberHooks.new.js#L1320

Link to code example: https://codesandbox.io/s/reacthooks-fkihz?file=/src/MaybeABug.js

The current behavior

The first time click the button, it will not print the "effect"
But the second click will print the "effect"
the third will not, the fourth will be and so on

The expected behavior

Maybe should always print nothing!

Remarks

The reason for this phenomenon is when component entering re-render phase, the effect object is a big different in updateQueue and Hook object. But i think should be consistent。

In fact, i am not sure if it is a bug, so I look forward to receiving a reply, thanks

Activity

  1. changed the title [-]Bug: [/-] [+]Bug: FunctionComponent re-render phase cause a bug[/+] on Jan 27, 2021
  2. eps1lon commented on Jan 27, 2021

    @eps1lon
    Collaborator

    Thanks for the report.

    React version:v17.0.1

    The codesandbox is using 0.0.0-experimental-4ead6b530. Could you clarify for which version you want to report the bug?

  3. careyke commented on Jan 27, 2021

    @careyke
    Author

    Thanks for the report.

    React version:v17.0.1

    The codesandbox is using 0.0.0-experimental-4ead6b530. Could you clarify for which version you want to report the bug?

    I tried it in v17.0.1 and this problem still occurs, because the function updateEffectImpl has not changed.

  4. eps1lon commented on Jan 27, 2021

    @eps1lon
    Collaborator

    Test illustrating the current behavior on master:

    it('yields inconsistently when triggering state updates during render', () => {
      let handleClick;
      function Test() {
        const [count, setCount] = useState(0);
    
        useEffect(() => {
          Scheduler.unstable_yieldValue(`Effect: ${count}`);
        }, [count]);
    
        if (count > 0) {
          setCount(0);
        }
    
        handleClick = () => setCount(2);
    
        return <Text text={`Render: ${count}`} />;
      }
    
      ReactNoop.act(() => {
        ReactNoop.render(<Test />);
      });
    
      expect(Scheduler).toHaveYielded(['Render: 0', 'Effect: 0']);
    
      ReactNoop.act(() => {
        handleClick();
      });
    
      expect(Scheduler).toHaveYielded(['Render: 0']);
    
      ReactNoop.act(() => {
        handleClick();
      });
    
      expect(Scheduler).toHaveYielded(['Render: 0', 'Effect: 0']);
    
      ReactNoop.act(() => {
        handleClick();
      });
    
      expect(Scheduler).toHaveYielded(['Render: 0']);
    });

    I would've expected expect(Scheduler).toHaveYielded(['Render: 0', 'Effect: 0']); to pass after every handleClick but every second click only yields ['Render: 0'] Not sure why I expected this. We only commit with unchanged deps so we should never run the effect. Yielding on every click with ['Render: 0'] is the correct answer.

    I tried it in v17.0.1 and this problem still occurs, because the function updateEffectImpl has not changed.

    Could you either update the issue or the codesandbox so that we avoid potential confusion when the bug is reported for one version but the repro uses a different one?

  5. careyke commented on Jan 27, 2021

    @careyke
    Author

    Could you either update the issue or the codesandbox so that we avoid potential confusion when the bug is reported for one version but the repro uses a different one?

    yeah! i have updated the version of React in the codesandbox

  6. gaearon commented on Jan 27, 2021

    @gaearon
    Contributor

    Is the behavior new in some version, or always been like this?

  7. removed
    Status: UnconfirmedA potential issue that we haven't yet confirmed as a bug
    on Jan 27, 2021
  8. eps1lon commented on Jan 27, 2021

    @eps1lon
    Collaborator

    Is the behavior new in some version, or always been like this?

    It always has been™. Tested with a codesandbox using 16.8.

  9. eps1lon commented on Jan 29, 2021

    @eps1lon
    Collaborator

    @careyke Thanks for the repro! This helped a lot identifying the issue.

  10. careyke commented on Jan 30, 2021

    @careyke
    Author

    You are welcome! i am very happy to do this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions