testing-library / react-hooks-testing-library

🐏 Simple and complete React hooks testing utilities that encourage good testing practices.
https://react-hooks-testing-library.com
MIT License
5.26k stars 233 forks source link

[renderHook] rerender with new props, new props are not consumed #964

Open alextrastero opened 1 year ago

alextrastero commented 1 year ago

Passing new props to rerender doesn't work, please let me know if i'm doing anything wrong, thx

package.json

"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",

hook

export const useTriggerFocus = ({ condition = true, ref }: any): null => {
  console.log(condition);
  useEffect(() => {
    const element = ref.current;

    if (condition && element) {
      element?.focus();
    }
  }, [ref, condition]);

  return null;
};

test

  it('triggers a change', () => {
    const condition = false;
    const focus = vi.fn();
    const ref = {
      current: {
        focus,
      },
    };

    const { rerender } = renderHook(() => useTriggerFocus({ condition, ref }));

    rerender({ condition: true, ref: undefined });
    rerender();
    rerender();
    rerender();
  });
});

console

false
false
false
false
false
cmorten commented 1 year ago

Care needed to ensure that your renderHook is configured such that it knows what to do with the new props you pass into the rerender call.

E.g. something along the lines of (not tested)…

  it('triggers a change', () => {
    const initialProps = {
      condition: false,
      ref: {
        current: {
          focus: vi.fn(),
        },
      },
    };

    const { rerender } = renderHook(({ condition, ref }) => useTriggerFocus({ condition, ref }), { initialProps });

    rerender({ condition: true, ref: undefined });
  });
});

See the docs https://testing-library.com/docs/react-testing-library/api#rerender-1 for the syntax on how to make sure new props are used. Here I’ve also made use of the initialProps option https://testing-library.com/docs/react-testing-library/api#renderhook-options-initialprops.

At the moment your props passed to the rerender and being ignored as the renderHook callback is hardcoded to use those you defined at the beginning of your test.

P.S. your future queries for React Testing Library may be better placed in https://github.com/testing-library/react-testing-library or the discord than here as this is the Dom Testing Library repo! 🙃

P.P.S. take care when using a ref with useEffect - in a lot of use cases it doesn’t necessarily work, see https://epicreact.dev/why-you-shouldnt-put-refs-in-a-dependency-array/

timdeschryver commented 1 year ago

This seems like a React Hooks Testing Library issue, I'm transferring this issue to the correct repository.

alextrastero commented 1 year ago

Thanks @timdeschryver , do you have a link for that?

timdeschryver commented 1 year ago

@alextrastero you don't have to take any action, we've already transferred it to the correct repository. You can find it here, https://github.com/testing-library/react-hooks-testing-library

mpeyper commented 1 year ago

Hey, sorry I missed the notification that this one was transferred here.

Sorry to play ping pong again with your issue, but can you confirm if you are using renderHook from @testing-library/react-hooks (pre React 18, this repo) or @testing-library/react (React 18, RTL repo).

If it’s the latter, I’ll transfer it to the RTL repo.

Edit: quickly looking at the issue as if it was for our version, I’d say the solution presented by @cmorten above is correct.

wellermiranda commented 11 months ago

That worked for me:

const { rerender } = renderHook(useOverview, { initialProps });
cmorten commented 11 months ago

Update: Ah I was too slow, as you’ve posted!


The intended pattern is:

const { result, rerender } = renderHook((props = {}) => useMyHook(props), {
  initialProps,
});

expect(result.current).toEqual(firstResult);

rerender(newProps);

expect(result.current).toEqual(secondResult)

There should be no need to workaround - the key is to not hardcode the initialProps being passed to your hook, but instead allow props to be passed to the renderHook callback so passing values into rerender works as expected.

See the example in https://testing-library.com/docs/react-testing-library/api/#renderhook-options-initialprops, though appreciate it perhaps isn’t the most clear as the “hook” in the example is the identity function of reflecting the props back rather than something explicitly use* as most folks will implement.

wellermiranda commented 11 months ago

Thanks, @cmorten.

NotNikita commented 7 months ago

Thanks, @cmorten.