theoavoyne

theoavoyne

Member Since 4 years ago

Paris

Experience Points
6
follower
Lessons Completed
3
follow
Lessons Completed
5
stars
Best Reply Awards
32
repos

880 contributions in the last year

Pinned
⚡ ResumeFactory is an online resume builder that can turn your LinkedIn into an editable and downloadable PDF.
⚡ Gamiflex is an application that provides the users with automatic predictions about video games they might like by cross-referencing the preferences of all users.
⚡ Montresor is a MVP of a Marketplace where you can buy and sell all kinds of stuff in Montreal. It was built during Le Wagon bootcamp.
⚡ Default configuration for Le Wagon's students
⚡ Ruby Snakes game using GOSU game development library.
⚡ Clean Javascript click game
Activity
Nov
24
6 days ago
Activity icon
fork

theoavoyne forked remix-run/react-router

⚡ Declarative routing for React
theoavoyne MIT License Updated
fork time in 5 days ago
Nov
21
1 week ago
Activity icon
fork

theoavoyne forked microsoft/TypeScript-Website

⚡ The Website and web infrastructure for learning TypeScript
theoavoyne Updated
fork time in 1 week ago
Nov
16
2 weeks ago
pull request

theoavoyne pull request davidtheclark/react-aria-modal

theoavoyne
theoavoyne

[Minor change] Changed cursor in dialogStyle

Set cursor: 'auto' instead of cursor: 'default' in dialogStyle. We should leave the browser decide what cursor to use based on the current context (e.g., text when hovering text) and not force it to use the default one.

push

theoavoyne push theoavoyne/react-aria-modal

theoavoyne
theoavoyne

[Minor change] Changed cursor in dialogStyle

Set cursor: 'auto' instead of cursor: 'default' in dialogStyle. We should leave the browser decide what cursor to use based on the current context (e.g., text when hovering text) and not force it to use the default one.

commit sha: a9d9e5a066371a8867357cde8b79225c62e90b59

push time in 1 week ago
Activity icon
fork

theoavoyne forked davidtheclark/react-aria-modal

⚡ A fully accessible React modal built according WAI-ARIA Authoring Practices
theoavoyne MIT License Updated
fork time in 1 week ago
Nov
15
2 weeks ago
Activity icon
issue

theoavoyne issue comment davidtheclark/react-aria-modal

theoavoyne
theoavoyne

Fix warning messages

  • Move componentWillMount content to constructor to avoid deprecation message.
  • Update babel preset-env to remove browserslist update message.
theoavoyne
theoavoyne

I'm using 4.0.1 and I'm still getting the warnings.

Nov
14
2 weeks ago
Activity icon
issue

theoavoyne issue comment firebase/firebase-tools

theoavoyne
theoavoyne

[Firestore Emulator] onSnapshot triggered with a nonexistent doc on doc creation

[REQUIRED] Environment info

firebase: 9.1.2

firebase-tools: 9.21.0

Platform: macOS Big Sur (11.6)

[REQUIRED] Test case

I'm experiencing a bug using the Firestore Emulator. On document creation, the onSnapshot listener (that was set up beforehand) is triggered twice:

  • The first time with the expected document (e.g. the one that was just created). It has the hasPendingWrites metadata property set to true, which is expected.
  • The second time with an empty document (snapshot.exists() is false), whereas the document was successfully created and is visible in the Emulator UI. The hasPendingWrites metadata is false in this case, which is expected. If I refresh the page however, the document is correctly fetched.

This never happened to me in production so I think it's an emulator-specific issue. Also, it seems to happen only once, when the emulator was just started. In other words, subsequent document creations seem not to be affected by this bug.

[REQUIRED] Steps to reproduce

N/A

[REQUIRED] Expected behavior

I'm expecting the snapshot attached the second onSnapshot triggering (the hasPendingWrites: false one) to exist and its data to match the created document.

[REQUIRED] Actual behavior

See above.

theoavoyne
theoavoyne

I wanted to create a minimal reproduction but the problem has been absolutely impossible to narrow down. I just spent 72 hours on this. There seem to be a kind of race condition that involves either the cache, the Emulator start up, the way transactions are handled, the reload method, or a mix between those, that causes the onSnapshot listener to be triggered with inaccurate data. It's just so unpredictable that I can't properly identify the origin. I'd love to be in touch with someone in the team and discuss the matter live with my code. It's pretty simple, and as you guys know how it works under the hood, you might be able to see what edge case I've reached.

Nov
11
2 weeks ago
Activity icon
issue

theoavoyne issue comment facebook/react

theoavoyne
theoavoyne

React DevTools recording commit without any component re-render

I'm struggling to make an isolated example of this, but the app where I found this is pretty simple so hopefully it's not too challenging to track down.

So I was profiling https://the-react-bookshelf.netlify.com (locally) and got this when I clicked on the "login" button:

image

image

The fact that there was no profile data for a commit is interesting. Each commit should be associated to a state update somewhere in the tree, and wherever that happened should trigger at least one component to re-render, but that didn't appear to happen here.

I also verified that I don't have any components filtered out:

image

And I didn't filter any commits either:

image

Here's the exported profile data:

https://gist.github.com/kentcdodds/dbff66043653333cd22cb9261a08550b

And here's the repo where you can pull it down and reproduce yourself: https://github.com/kentcdodds/bookshelf. The component we're looking at is here: https://github.com/kentcdodds/bookshelf/blob/master/src/unauthenticated-app.js

Sorry I can't give a more direct reproduction.

theoavoyne
theoavoyne

Such a change would break e.g. component inspection if a change in props didn't result in any effects.

That's pretty bad I think. It will make the Profiler less predictable.

Even cheap renders aren't free and these bailouts have cost, so maybe we should report them.

Totally agree.

In the end, I'm not 100% sure that the Profiler is doing anything wrong here. If the extra render caused by the second call to setState wasn't happening, its output would be what's expected and we would never (or maybe it happens in other scenarios? I'm not sure) encounter such an empty tree.

BTW, this issue was discussed here. Unfortunately, it wasn't clearly answered.

PS: I'm not super familiar with the codebase, so I may not be of great assistance here. Still, I'm digging into it.

Nov
10
2 weeks ago
Activity icon
issue

theoavoyne issue comment facebook/react

theoavoyne
theoavoyne

React DevTools recording commit without any component re-render

I'm struggling to make an isolated example of this, but the app where I found this is pretty simple so hopefully it's not too challenging to track down.

So I was profiling https://the-react-bookshelf.netlify.com (locally) and got this when I clicked on the "login" button:

image

image

The fact that there was no profile data for a commit is interesting. Each commit should be associated to a state update somewhere in the tree, and wherever that happened should trigger at least one component to re-render, but that didn't appear to happen here.

I also verified that I don't have any components filtered out:

image

And I didn't filter any commits either:

image

Here's the exported profile data:

https://gist.github.com/kentcdodds/dbff66043653333cd22cb9261a08550b

And here's the repo where you can pull it down and reproduce yourself: https://github.com/kentcdodds/bookshelf. The component we're looking at is here: https://github.com/kentcdodds/bookshelf/blob/master/src/unauthenticated-app.js

Sorry I can't give a more direct reproduction.

theoavoyne
theoavoyne

And don't you think that the problem may lie here, e.g. that React renders when it shouldn't?

I use console.log on rendering quite a lot for debugging purposes, and I've never encountered a situation where it was fired unexpectedly.

I agree that the Profiler should be showing commits only when they happened, so your PR is much needed. But maybe this is pointing to another problem as well? Maybe the event system (since it happens only with event handlers) is messing up at some point?

Activity icon
issue

theoavoyne issue comment facebook/react

theoavoyne
theoavoyne

React DevTools recording commit without any component re-render

I'm struggling to make an isolated example of this, but the app where I found this is pretty simple so hopefully it's not too challenging to track down.

So I was profiling https://the-react-bookshelf.netlify.com (locally) and got this when I clicked on the "login" button:

image

image

The fact that there was no profile data for a commit is interesting. Each commit should be associated to a state update somewhere in the tree, and wherever that happened should trigger at least one component to re-render, but that didn't appear to happen here.

I also verified that I don't have any components filtered out:

image

And I didn't filter any commits either:

image

Here's the exported profile data:

https://gist.github.com/kentcdodds/dbff66043653333cd22cb9261a08550b

And here's the repo where you can pull it down and reproduce yourself: https://github.com/kentcdodds/bookshelf. The component we're looking at is here: https://github.com/kentcdodds/bookshelf/blob/master/src/unauthenticated-app.js

Sorry I can't give a more direct reproduction.

theoavoyne
theoavoyne

Thank you, Brian.

Yes I tried that already and noticed exactly what you said. But why is React rendering on the second blur anyway? From my understanding, it shouldn't. And why is it rendering on the second blur but not on subsequent ones? What happens with the subsequent ones (i.e. no rendering at all) is what I expect should happen with the second one.

Activity icon
issue

theoavoyne issue comment facebook/react

theoavoyne
theoavoyne

React DevTools recording commit without any component re-render

I'm struggling to make an isolated example of this, but the app where I found this is pretty simple so hopefully it's not too challenging to track down.

So I was profiling https://the-react-bookshelf.netlify.com (locally) and got this when I clicked on the "login" button:

image

image

The fact that there was no profile data for a commit is interesting. Each commit should be associated to a state update somewhere in the tree, and wherever that happened should trigger at least one component to re-render, but that didn't appear to happen here.

I also verified that I don't have any components filtered out:

image

And I didn't filter any commits either:

image

Here's the exported profile data:

https://gist.github.com/kentcdodds/dbff66043653333cd22cb9261a08550b

And here's the repo where you can pull it down and reproduce yourself: https://github.com/kentcdodds/bookshelf. The component we're looking at is here: https://github.com/kentcdodds/bookshelf/blob/master/src/unauthenticated-app.js

Sorry I can't give a more direct reproduction.

theoavoyne
theoavoyne

@bvaughn Are we sure it's caused by the Profiler?

Take this code:

const App = () => {
  const [, setTouched] = useState(false);

  const handleBlur = useCallback(() => {
    setTouched(true);
  }, []);

  console.log('Rendered');

  return (
    <input onBlur={handleBlur} />
  )
};

If you click in and out the input twice, you'll notice 'Rendered' gets printed twice to the console, and not only once as we might expect. Opening the Profiler, you'll see two commits, the first one is normal (and expected), the other one is ghosty as above. Weirdly enough, it's just the second blur that causes the extra print and commit, i.e. if you repeat the operation one or more times, nothing will happen.

Doesn't the fact that the App element actually ran (even without causing a re-render) show that the Profiler is not faulty here, and that maybe the event system is? I may be missing something, forgive me if I am!

Sandbox link.

Nov
9
3 weeks ago
Activity icon
issue

theoavoyne issue comment firebase/firebase-tools

theoavoyne
theoavoyne

[Firestore Emulator] onSnapshot triggered with a nonexistent doc on doc creation

[REQUIRED] Environment info

firebase: 9.1.2

firebase-tools: 9.21.0

Platform: macOS Big Sur (11.6)

[REQUIRED] Test case

I'm experiencing a bug using the Firestore Emulator. On document creation, the onSnapshot listener (that was set up beforehand) is triggered twice:

  • The first time with the expected document (e.g. the one that was just created). It has the hasPendingWrites metadata property set to true, which is expected.
  • The second time with an empty document (snapshot.exists() is false), whereas the document was successfully created and is visible in the Emulator UI. The hasPendingWrites metadata is false in this case, which is expected. If I refresh the page however, the document is correctly fetched.

This never happened to me in production so I think it's an emulator-specific issue. Also, it seems to happen only once, when the emulator was just started. In other words, subsequent document creations seem not to be affected by this bug.

[REQUIRED] Steps to reproduce

N/A

[REQUIRED] Expected behavior

I'm expecting the snapshot attached the second onSnapshot triggering (the hasPendingWrites: false one) to exist and its data to match the created document.

[REQUIRED] Actual behavior

See above.

theoavoyne
theoavoyne

Of course. I'll put up a demo later today or tomorrow.

Nov
6
3 weeks ago
Activity icon
issue

theoavoyne issue facebook/react

theoavoyne
theoavoyne

Bug: when initialArg changes, useReducer should update the state accordingly

TL;DR: After the first render, useReducer doesn't react to changes in the initialArg (second positional) argument. In my opinion, it should update the state accordingly. The actual behaviour in unnecessarily restrictive and forces us to rely on hacks to address the problems it brings.

Let's take the example of a form provider, a component that enables us to make javascript objects easily editable by users through inputs:

// App.js

const users = {
  1: {
    firstName: 'Paul',
    lastName: 'Atreides',
  },
  2: {
    firstName: 'Duncan',
    lastName: 'Idaho',
  },
};

const App = () => {
  const [id, setId] = useState(1);

  return (
    <>
      <div>Pick User</div>
      <button onClick={() => { setId(1); }} type="button">User 1</button>
      <button onClick={() => { setId(2); }} type="button">User 2</button>
      <FormProvider initialValues={users[id]}>
        <Editor />
      </FormProvider>
    </>
  );
};
// FormProvider.js

const reducer = (state, action) => {
  switch (action.type) {
    case 'UPDATE_FIELD':
      return { ...state, [action.field]: action.value };
    default:
      throw new Error();
  }
};

const FormProvider = ({ children, initialValues }) => {
  const [values, dispatch] = useReducer(reducer, initialValues);

  const handleChange = useCallback((evt) => {
    dispatch({
      field: evt.target.name,
      type: 'UPDATE_FIELD',
      value: evt.target.value,
    });
  }, []);

  return (
    <FormContext.Provider value={{ handleChange, values }}>
      {children}
    </FormContext.Provider>
  );
};
// Editor.js

const Editor = () => {
  const { handleChange, values } = useContext(FormContext);

  return (
    <>
      <div>First name:</div>
      <input
        name="firstName"
        onChange={handleChange}
        value={values.firstName}
      />
      <div>First name:</div>
      <input
        name="lastName"
        onChange={handleChange}
        value={values.lastName}
      />
    </>
  );
};

React version: 17.0.2

Steps To Reproduce

  1. Click on the "User 2" button.
  2. Notice nothing happens.

Link to code example: https://codesandbox.io/s/wandering-cache-4vwwe?file=/src/App.js.

The current behavior

When clicking on the "User 2" button, nothing happens. As far as I understand, this is by design. I believe you guys were afraid that people would forget to keep the identity of initialArg stable, which would lead to the state being accidentally reset on every cycle. This does make sense, however it makes composition more difficult for developers who know what they are doing and who do want the state to be updated whenever initialArg changes.

Today, here are my two options:

1. Adding a key prop to <FormProvider />

<FormProvider key={id} initialValues={users[id]}>

This will cause the entire component (and its children) to be unmounted/remounted. The useReducer hook will thus initialise from scratch, with the correct initialValues. Whereas this does work, it's pretty bad in terms of performance and I mean ... it's an ugly hack.

2. Dispatch a RESET action whenever initialValues changes

// FormProvider.js

const reducer = (state, action) => {
  switch (action.type) {
    case 'UPDATE_FIELD':
      return { ...state, [action.field]: action.value };
    case 'RESET':
      return action.values;
    default:
      throw new Error();
  }
};
const FormProvider = ({ children, initialValues }) => {
  // ...

  const isFirstRenderRef = useRef(true);

  useEffect(() => {
    if (!isFirstRenderRef.current) {
      dispatch({
        type: 'RESET',
        values: initialValues,
      });
    }
  }, [initialValues]);

  useEffect(() => {
    isFirstRenderRef.current = false;
  }, []);

 // ...
};

This will indeed trigger the updating of the state. However, it won't happen before the next cycle. It means that there'll be a moment where the <FormProvider> state will not be mirroring the user's selected profile. Should he decide to update a field during this fraction of a second and it will be an absolute, unforgettable disaster.

The expected behavior

This problem wouldn't exist if the useReducer state was updating when changes in initialArg occur. This is also the most natural behaviour. It would do no harm to developers who want the state not to be updated even though initialArg changes (god, who are these guys ?!), as they might use useState or useRef (useReducer(reducer, useRef(initialValues).current)).

Nov
5
3 weeks ago
Activity icon
issue

theoavoyne issue comment remix-run/react-router

theoavoyne
theoavoyne

Permanent redirect with Navigate (V6)

I noticed that the new <Navigate> element doesn't have the from prop that was pretty useful when used in the <Redirect> element (V5) for setting up a permanent redirect (e.g. / to /dashboard). It seems anyway that a <Navigate> element can't be used directly inside a <Routes> element—whereas a similar behaviour was possible in V5 using <Switch>/<Redirect>.

Is the code below the recommended way to go to mimic the old behaviour?

<Route element={<Navigate to="/dashboard" />} index /> // index can be replaced with path="/anything"

If yes, it'd be worth mentioning that in the v5-to-v6 migration doc. I can prepare a PR if needed.

theoavoyne
theoavoyne

@mjackson Thanks for the clarifying this. You're completely right.

Nov
1
4 weeks ago
Activity icon
issue

theoavoyne issue remix-run/react-router

theoavoyne
theoavoyne

Permanent redirect with Navigate (V6)

I noticed that the new <Navigate> element doesn't have the from prop that was pretty useful when used in the <Redirect> element (V5) for setting up a permanent redirect (e.g. / to /dashboard). It seems anyway that <Navigate> can't be used directly inside <Routes>—whereas a similar behaviour was possible in V5 using <Switch>/<Redirect>.

Is the code below the recommended way to go to mimic the old behaviour?

<Route element={<Navigate to="/dashboard" />} index /> // index can be replaced with path="/anything"

If yes, it'd be worth mentioning that in the v5-to-v6 migration doc. I can prepare a PR if needed.

Activity icon
issue

theoavoyne issue comment remix-run/react-router

theoavoyne
theoavoyne

Removed history from install scripts

History is now a regular dependency. So these are not needed, right?

theoavoyne
theoavoyne

I may be wrong, but I guess that most people don't use the history package directly? So it'd be weird that they have it in their direct dependencies and that they have to keep it up to date themselves. And if people are trying to import stuff from history while not having the package listed in their dependencies, they're the one not being careful. A proper linter will warn them anyway (eslint's import/no-extraneous-dependencies rule).

Oct
31
4 weeks ago
push

theoavoyne push theoavoyne/react-router

theoavoyne
theoavoyne

Removed history from install scripts.

commit sha: 98f5d8b4df8e7094328bf075303c8085c596ee9a

push time in 4 weeks ago
pull request

theoavoyne pull request remix-run/react-router

theoavoyne
theoavoyne

Removed history from install scripts

History is now a regular dependency. So these are not needed, right?

push

theoavoyne push theoavoyne/react-router

theoavoyne
theoavoyne

Removed history from install scripts.

commit sha: 530938cbad8d2ce0bb52c50dd3ffff3f9ca110f3

push time in 4 weeks ago
push

theoavoyne push theoavoyne/react-router

theoavoyne
theoavoyne

Removed history from install scripts

History is now a regular dependency. So this is not needed, right?

commit sha: 99ec7007d77fbc7ac9a38a6407ccdeed1d65dc10

push time in 4 weeks ago
Activity icon
fork

theoavoyne forked remix-run/react-router

⚡ Declarative routing for React
theoavoyne MIT License Updated
fork time in 4 weeks ago
Oct
26
1 month ago
Activity icon
issue

theoavoyne issue firebase/firebase-tools

theoavoyne
theoavoyne

[Firestore Emulator] onSnapshot triggered with a nonexistent doc on doc creation

[REQUIRED] Environment info

firebase-tools: 9.21.0

Platform: macOS Big Sur (11.6)

[REQUIRED] Test case

I'm experiencing a bug using the Firestore Emulator. On document creation, the onSnapshot listener (that was set up beforehand) is triggered twice:

  • The first time with the expected document (e.g. the one that was just created). It has the hasPendingWrites metadata property set to true, which is expected.
  • The second time with an empty document (snapshot.exists() is false), whereas the document was successfully created and is visible in the Emulator UI. The hasPendingWrites metadata is false in this case, which is expected. If I refresh the page however, the document is correctly fetched.

This never happened to me in production so I think it's an emulator-specific issue. Also, it seems to happen only once, when the emulator was just started. In other words, subsequent document creations seem not to be affected by this bug.

[REQUIRED] Steps to reproduce

N/A

[REQUIRED] Expected behavior

I'm expecting the snapshot attached the second onSnapshot triggering (the hasPendingWrites: false one) to exist and its data to match the created document.

[REQUIRED] Actual behavior

See above.

Oct
13
1 month ago
Activity icon
issue

theoavoyne issue remix-run/react-router

theoavoyne
theoavoyne

[Bug]:

What version of React Router are you using?

5.3.0

Steps to Reproduce

This works:

const Router = () => (
    <Switch>
        <Route component={() => <div>Route1</div>} exact path="/route1" />
        <Route component={() => <div>Route2</div>} exact path="/route2" />
    </Switch>
);

This doesn't work (Route2 in unreachable):

const Routes1 = () => (
  <Route component={() => <div>Route1</div>} exact path="/route1" />
);

const Routes2 = () => (
  <Route component={() => <div>Route2</div>} exact path="/route2" />
);

const Router = () => (
    <Switch>
        <Routes1 />
        <Routes2 />
    </Switch>
);

Is there any workaround (apart from not using Switch)? I'd like to split my big Router component containing all my routes into smaller, logical groups of routes.

Expected Behavior

Expecting Route2 to show up when path is /route2.

Actual Behavior

Well... Route2 is not showing up when path is /route2. There's no error but the first element, <Route1 />, is matched. Nothing renders of course because of path="/route1" in Route1.

Activity icon
issue

theoavoyne issue remix-run/react-router

theoavoyne
theoavoyne

[Bug]:

What version of React Router are you using?

5.3.0

Steps to Reproduce

This works:

const Router = () => (
    <Switch>
        <Route component={() => <div>Route1</div>} exact path="/route1" />
        <Route component={() => <div>Route2</div>} exact path="/route2" />
    </Switch>
);

This doesn't work (Route2 in unreachable):

const Routes1 = () => (
  <Route component={() => <div>Route1</div>} exact path="/route1" />
);

const Routes2 = () => (
  <Route component={() => <div>Route2</div>} exact path="/route2" />
);

const Router = () => (
    <Switch>
        <Routes1 />
        <Routes2 />
    </Switch>
);

Is there any workaround (apart from not using Switch)? I'd like to split my big Router component containing all my routes into smaller, logical groups of routes.

Expected Behavior

Expecting Route2 to show up when path is /route2.

Actual Behavior

Well... Route2 is not showing up when path is /route2. There's no error but the first element, <Route1 />, is matched. Nothing renders of course because of path="/route1" in Route1.

Aug
31
2 months ago
Activity icon
issue

theoavoyne issue comment formium/formik

theoavoyne
theoavoyne

Check didMount in setStatus to avoid calling setState after Formik unmounts

I'm currently using Formik in my project (loving it), but ran into an issue recently.

Problem

All of the onSubmit handlers in my app return a promise. I wrap each handler using a common HOF like so, to handle the submitting and status state in a consistent way when the promise resolves/rejects:

export const handleSubmit = (onSubmit) => (data, {
	setStatus,
	setSubmitting,
}) => {
	setStatus(null);
	return onSubmit(data)
		.then((result) => setStatus(result))
		.catch((error) => {
			const { message: id, values } = error;
			setStatus({ type: 'error', message: { id, values } });
		})
		.finally(() => setSubmitting(false));
};

Some of those onSubmit handlers result in a route change on success, which unmounts the Formik instance. I'm using the Next.js router to handle the route change, so in this case I return the promise returned by the router's push/replace methods. This is a lot of detail to say: The then, catch, and finally handlers above end up firing after the route change — that is, after the Formik instance has unmounted.

This is actually not a problem for the finally handler because setSubmitting (very smartly) guards against calling setState after the component has unmounted — but not so for setStatus 😞 (nor for any of the other setters available in the FormikBag).

Proposed Solution

setStatus = (status?: any) => {
    if (this.didMount) {
        this.setState({ status });
    }
};
theoavoyne
theoavoyne

Recap: don't

onSubmit={(values, actions) => {
  login({ variables: values }).then(() =>
    actions.setSubmitting(false)
  );
}}

The only problem here @t-lock is that you're not returning the promise. If you don't return anything the function just returns undefined. That's why Formik can't know this is asynchronous code. There's no need for the async syntax in this specific case. Just:

onSubmit={(values, actions) => {
  return login({ variables: values }).then(() =>
    actions.setSubmitting(false)
  );
}}