Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Allow setState inside willUpdate and init #139

Merged
merged 9 commits into from
Aug 7, 2018

Conversation

LPGhatguy
Copy link
Contributor

@LPGhatguy LPGhatguy commented Jul 30, 2018

This change lays a little bit of groundwork for allowing setState in more places.

Right now, setState is disallowed in all lifecycle hooks and render with the exception of didMount. I think it's safe to expand this to allow willUpdate as well, predicated on the behavior of setState not causing a re-render at that point, but instead updating the existing state.

This PR adds _setStateWithoutUpdate next to _setStateBlockedReason, which changes the behavior of a state update.

Checklist before submitting:

  • Added entry to CHANGELOG.md
  • Added/updated relevant tests
  • Added/updated documentation

@LPGhatguy
Copy link
Contributor Author

This probably isn't quite ready (tests, docs, and changelog entry needed still), but probably ready for someone to see if this is sound.

@ZoteTheMighty
Copy link
Contributor

ZoteTheMighty commented Jul 30, 2018

This is interesting. Is this a departure from the direction React is going in with respect to their newer versions of lifecycle methods like getDerivedStateFromProps?

Is the idea that it's better to adjust the re-rendering rules to account for situations where this is useful, rather than trying to build ways around the way they already are? Does it jive with your plans for the new reconciler in some way?

@LPGhatguy
Copy link
Contributor Author

The original intent with locking down setState in all of our lifecycle methods was mostly trying to be as conservative as possible until we figure out the way to go.

React handles setState differently based on when in the pipeline it's called. When it's called when there's already a render scheduled, it just updates the state that the next render will use. That lines up with what I wanted to do with the new (async) reconciler, though the implementation will be drastically different there.

@ZoteTheMighty
Copy link
Contributor

Okay, I think I see where you're coming from. It definitely would be good to have fewer situations where users have to worry about creating infinite re-rendering scenarios, and I suspect setState in willUpdate provides a more natural way to do the sorts of things you'd do in getDerivedStateFromProps.

This looks fine so far, I'll look again and review when you tick some boxes.

@LPGhatguy LPGhatguy changed the title Allow setState inside willUpdate Allow setState inside willUpdate and init Aug 6, 2018
@coveralls
Copy link

coveralls commented Aug 6, 2018

Coverage Status

Coverage increased (+0.03%) to 89.341% when pulling c78b760 on setstate-inside-lifecycle into d718e62 on master.

Copy link
Contributor

@ZoteTheMighty ZoteTheMighty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly clarification/wording stuff for the docs

end
```

In older versions of Roact, `setState` was disallowed in `init`, and you would instead assign to `state` directly. This is still acceptable, but it's simpler to use `setState`:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wording right before the colon here is a bit confusing as it sounds like you're about to demonstrate use of setState. Could you construct the sentence differently? Maybe "It's simpler to use setState, but it's still acceptable to assign it directly:"

* Lifecycle hooks: `willUnmount`
* Pure functions: `render`, `shouldUpdate`

Calling `setState` inside of `init` or `willUpdate` has different behavior in other places. Because Roact is already going to render or update a component in these cases, the update will be replaced instead of another update happening.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the "in other places" part (also, you're missing a "than"). It might be fine to just say "...has special behavior." What do you think?


In this case, we're passing a _function_ to `setState`. This function is called and passed the current state, and returns a new state. It can also return `nil` to abort the state update, which lets Roact make some handy optimizations.

Right now, this version of `setState` is exactly the same as the form where we pass an object. In the future, Roact will support a number of features, like asynchronous rendering, that make the distinction more important.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd maybe say "works exactly the same"

end).to.throw()
local handle = Reconciler.mount(testElement)

expect(forceUpdate).to.be.a("function")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a problem, but I'm curious why you added this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thought process was that this expectation is sort of implicit, and if it ever failed, the error message would be not-so-great. Thinking about it now, I think I'll remove it.

Copy link
Contributor

@ZoteTheMighty ZoteTheMighty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Contributor

@AmaranthineCodices AmaranthineCodices left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM as well, for what it's worth

@LPGhatguy LPGhatguy merged commit 430a0e3 into master Aug 7, 2018
@LPGhatguy LPGhatguy deleted the setstate-inside-lifecycle branch August 7, 2018 00:20
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants