Skip to content

[Hooks] useEffect hook api advice #76

@xialvjun

Description

@xialvjun

copied from: #68 (comment)

hope for api like this

current api:

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

I think better api is:

useEffect(
  function didPatchDOM(prevProps){
    const subscription = props.source.subscribe();
    return function willPatchDOM(nextProps) {
      subscription.unsubscribe();
      return false; // false means do not run didPatchDOM, and use this old willPatchDOM to handle the nextNextProps.
    };
  }
);

// then
// componentDidMount
useEffect(function componentDidMount(prevProps) {
  assertEqual(prevProps, null);
  do_effects();
  return nextProps => false;
});

// componentWillUnmount
useEffect(function componentWillUnmount(prevProps) {
  return nextProps => {
    if (nextProps === null) {
      do_effects();
    }
  };
});

// the real subscription example
useEffect(function realSubscription(prevProps) {
  if (prevProps === null) {
    const subscription = props.source.subscribe();
    return nextProps => {
      if (nextProps === null) {
        subscription.unsubscribe();
      }
      return false;
    }
  }
  return function willNeverReach() { return false; }
})
  1. those functions name: didPatchDOM and willPatchDOM are just for documenting, not show the lifecycle;
  2. false means do not run didPatchDOM, and use this old willPatchDOM to handle the nextNextProps: so, prevProps and props in closure willPatchDOM may be objects of very early time, they are ancient;
  3. I don't know if it's a good idea: if willPatchDOM has no return, it returns true.

copied from: #68 (comment)

the changes between my thought api and current api are:

// current api:
useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

// my thought api:
useEffect(
  // 1. here is a param: prevProps
  (prevProps) => {
    const subscription = props.source.subscribe();
    // 2. here is a param: nextProps
    return (nextProps) => {
      subscription.unsubscribe();
      // 3. this returned function should return a boolean
      return false;
    };
  }
);
  1. prevProps: it should leave the comparing logic to us rather than the second param of useEffect even though the second param of useEffect is calculated by us in the SFC. If the element is just constructed, prevProps is null;
  2. nextProps: we need param nextProps to decide what should we do. If the element will unmount, nextProps is null;
  3. return a boolean: this boolean indicates should react run the effect function after it have patched the real DOM.
  4. the nextProps function, well, the function receives the nextProps: this function closure wraps many outside variables like props, prevProps. If it returns false, then it will not be updated, and in next render term, react will still run it, then things may be: prevProps -> props -> nextOneProps -> nextTwoProps -> ... -> nextNProps, and thenextProps is infact the nextNProps.

copied from: #68 (comment)

Oh, the function closure which receives the nextProps wraps so many outside variables like props, prevProps, it infact wrap state, setState too.

And since it may not be updated in one render term, the nextProps param is infact nextNProps, we have no means to get the next(N-1)State.

So, another advice:

let [state, setState, getState] = useState(xxx);

getState won't change, and it will always return the latest state.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions