Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request]: distinguish "what" and "when" dependencies in useEffect #176

Closed
jsamr opened this issue Sep 11, 2020 · 2 comments
Closed

Comments

@jsamr
Copy link

jsamr commented Sep 11, 2020

EDIT: closed in favor of facebook/react#19820.

Feature

A new overloading for useEffect (Typescript syntax):

interface useEffect {
  /**
   * @param what - what this side effect does?
   * @param whatDeps - which variables modify “what” the side effect does?
   * These dependencies must match all live variables explicitly referenced
   * in the body of the “what” callback.
   * @param whenDeps - which variables modify “when” the side effect takes place?
   * When and only when at least one of those dependencies change, the “what”
   * callback should be executed.
   */
  (what: (...args: any[]) => any, whatDeps: any[], whenDeps: any[]): void;
  /**
   * @param what - what this side effect does?
   * @param deps - an array of values that the effect depends on.
   *
   */
  (what: (...args: any[]) => any, deps?: any[]): void;
}

Motivations

In the current implementation, the second argument of useEffect, “deps”, is described as such:

The array of values that the effect depends on.

This definition does not account for an important nuance between two kind of dependencies:

  • what dependencies, those which require the effect callback to be recomputed;
  • when dependencies, those which require the effect callback to be rerun.

The community seems to be in need of a solution, see https://stackoverflow.com/q/55724642/2779871.

Use case

I want to scroll to top of a component when the content changes (first dependency), but this effect also depends on a variable padding top (second dependency).

With the current implementation of useEffect, this is what I would do:

function MyComponent(props) {
  const { paddingTop, content } = props;
  const ref = React.useRef();
  React.useEffect(() => {
    // scroll to paddingTop when content changes?
    ref.current.scrollTo(0, paddingTop);
  }, [paddingTop, content]);
 return <div ref={ref}>...</div>
}

There is an undesired behavior: the hook is executed on paddingTop changes. Moreover, content is not, semantically, a dependency of the callback, but rather a dependency of when this side effect should take place. So I could use a ref, store the previous value of paddingTop, and compare the two. But that is cumbersome.

What I would like to do, is express the when this side-effect should take place dependencies declaratively:

function MyComponent(props) {
  const { paddingTop, content } = props;
  const ref = React.useRef();
  React.useEffect(() => {
    // scroll to paddingTop when content changes.
    ref.current.scrollTo(0, paddingTop);
  }, [paddingTop], [content]);
 return <div ref={ref}>...</div>
}

Detailed behavior

My understanding is that this proposal would not be a breaking change and is 100% retrocompatible with current implementation.

One argument

useEffect(what);

The behavior is identical to current implementation. The effect is executed after each render cycle.

Two arguments

useEffect(what, deps);

The behavior is identical to current implementation. The second argument conflates whatDeps and whenDeps.

Empty second argument

useEffect(what, []);

The behavior is identical to current implementation. The callback is executed only once.

Empty third argument

useEffect(what, whatDeps, []);

The callback is executed only once, regardless of the changes in whatDeps.

Three arguments

useEffect(what, whatDeps, whenDeps);

The callback is executed when and only when at least one variable in whenDeps array changes, regardless of the changes in whatDeps.

@jsamr jsamr mentioned this issue Sep 11, 2020
@gaearon
Copy link
Member

gaearon commented Sep 11, 2020

If you don't want to write an RFC, please start a discussion in the React repo. We don't monitor "issues" in this repository, it's solely for pull requests.

@jsamr
Copy link
Author

jsamr commented Sep 11, 2020

@gaearon All right I'll do that. I honestly don't know if my proposition is realistic (the "drawbacks" section in the RFC template) and I would rather engage in a discussion prior to writing a RFC.

@jsamr jsamr closed this as completed Sep 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants