-
Notifications
You must be signed in to change notification settings - Fork 47.6k
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
Add Transition Types #32105
Add Transition Types #32105
Conversation
Pass it to startViewTransition.
This lets you use types to trigger different imperative animations.
This adds navigation direction add Transition Types. This lets us specify an animation that cross-fades for normal push but slides left or right depending on if it's forward or back navigation.
…n type This is more convenient than the CSS form.
Comparing: 1185f88...0c00181 Critical size changesIncludes critical production bundles, as well as any change greater than 2%:
Significant size changesIncludes any change greater than 0.2%: Expand to show
|
if (nextIndex > previousIndex) { | ||
addTransitionType('navigation-forward'); | ||
} else if (nextIndex < previousIndex) { | ||
addTransitionType('navigation-back'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an example of how a canonical router implementation could add these types.
We could even go as far as just listening to Navigation Events ourselves in React and have these built-in but it's probably better to leave that up to routers since the intercepts could be doing things we're not aware of.
amazing,very good! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the path for getting test coverage for these view transition features? I'm assuming its difficult to mock within unit tests, hence extra fixture examples. And here transition types are only exposed to the view transition events. But any ideas on how to prevent future regressions here?
Is it possible that no renderer claims the types and .V ends up not getting cleared? |
Yea if you don't ever call I think we've had some early bailouts that might trigger if a render leads to no effects. We probably should do that more. |
This adds an isomorphic API to add Transition Types, which represent the cause, to the current Transition. This is currently mainly for View Transitions but as a concept it's broader and we might expand it to more features and object types in the future. ```js import { unstable_addTransitionType as addTransitionType } from 'react'; startTransition(() => { addTransitionType('my-transition-type'); setState(...); }); ``` If multiple transitions get entangled this is additive and all Transition Types are collected. You can also add more than one type to a Transition (hence the `add` prefix). Transition Types are reset after each commit. Meaning that `<Suspense>` revealing after a `startTransition` does not get any View Transition types associated with it. Note that the scoping rules for this is a little "wrong" in this implementation. Ideally it would be scoped to the nearest outer `startTransition` and grouped with any `setState` inside of it. Including Actions. However, since we currently don't have AsyncContext on the client, it would be too easy to drop a Transition Type if there were no other `setState` in the same `await` task. Multiple Transitions are entangled together anyway right now as a result. So this just tracks a global of all pending Transition Types for the next Transition. An inherent tricky bit with this API is that you could update multiple roots. In that case it should ideally be associated with each root. Transition Tracing solves this by associating a Transition with any updates that are later collected but this suffers from the problem mentioned above. Therefore, I just associate Transition Types with one root - the first one to commit. Since the View Transitions across roots are sequential anyway it kind of makes sense that only one really is the cause and the other one is subsequent. Transition Types can be used to apply different animations based on what caused the Transition. You have three different ways to choose from for how to use them: ## CSS It integrates with [View Transition Types](https://www.w3.org/TR/css-view-transitions-2/#active-view-transition-pseudo-examples) so you can match different animations based on CSS scopes: ```css :root:active-view-transition-type(my-transition-type) { &::view-transition-...(...) { ... } } ``` This is kind of a PITA to write though and if you have a CSS library that provide View Transition Classes it's difficult to import those into these scopes. ## Class per Type This PR also adds an object-as-map form that can be passed to all `className` properties: ```js <ViewTransition className={{ 'my-navigation-type': 'hello', 'default': 'world', }}> ``` If multiple types match, then they're joined together. If no types match then the special `"default"` entry is used instead. If any type has the value `"none"` then that wins and the ViewTransition is disabled (not assigned a name). These can be combined with `enter`/`exit`/`update`/`layout`/`share` props to match based on kind of trigger and Transition Type. ```js <ViewTransition enter={{ 'navigation-back': 'enter-right', 'navigation-forward': 'enter-left', }} exit={{ 'navigation-back': 'exit-right', 'navigation-forward': 'exit-left', }}> ``` ## Events In addition, you can also observe the types in the View Transition Event callbacks as the second argument. That way you can pick different imperative Animations based on the cause. ```js <ViewTransition onUpdate={(inst, types) => { if (types.includes('navigation-back')) { ... } else if (types.includes('navigation-forward')) { ... } else { ... } }}> ``` ## Future In the future we might expose types to `useEffect` for more general purpose usage. This would also allow non-View Transition based Animations such as existing libraries to use this same feature to coordinate the same concept. We might also allow richer objects to be passed along here. Only the strings would apply to View Transitions but the imperative code and effects could do something else with them. DiffTrain build for [028c8e6](028c8e6)
This adds an isomorphic API to add Transition Types, which represent the cause, to the current Transition. This is currently mainly for View Transitions but as a concept it's broader and we might expand it to more features and object types in the future. ```js import { unstable_addTransitionType as addTransitionType } from 'react'; startTransition(() => { addTransitionType('my-transition-type'); setState(...); }); ``` If multiple transitions get entangled this is additive and all Transition Types are collected. You can also add more than one type to a Transition (hence the `add` prefix). Transition Types are reset after each commit. Meaning that `<Suspense>` revealing after a `startTransition` does not get any View Transition types associated with it. Note that the scoping rules for this is a little "wrong" in this implementation. Ideally it would be scoped to the nearest outer `startTransition` and grouped with any `setState` inside of it. Including Actions. However, since we currently don't have AsyncContext on the client, it would be too easy to drop a Transition Type if there were no other `setState` in the same `await` task. Multiple Transitions are entangled together anyway right now as a result. So this just tracks a global of all pending Transition Types for the next Transition. An inherent tricky bit with this API is that you could update multiple roots. In that case it should ideally be associated with each root. Transition Tracing solves this by associating a Transition with any updates that are later collected but this suffers from the problem mentioned above. Therefore, I just associate Transition Types with one root - the first one to commit. Since the View Transitions across roots are sequential anyway it kind of makes sense that only one really is the cause and the other one is subsequent. Transition Types can be used to apply different animations based on what caused the Transition. You have three different ways to choose from for how to use them: ## CSS It integrates with [View Transition Types](https://www.w3.org/TR/css-view-transitions-2/#active-view-transition-pseudo-examples) so you can match different animations based on CSS scopes: ```css :root:active-view-transition-type(my-transition-type) { &::view-transition-...(...) { ... } } ``` This is kind of a PITA to write though and if you have a CSS library that provide View Transition Classes it's difficult to import those into these scopes. ## Class per Type This PR also adds an object-as-map form that can be passed to all `className` properties: ```js <ViewTransition className={{ 'my-navigation-type': 'hello', 'default': 'world', }}> ``` If multiple types match, then they're joined together. If no types match then the special `"default"` entry is used instead. If any type has the value `"none"` then that wins and the ViewTransition is disabled (not assigned a name). These can be combined with `enter`/`exit`/`update`/`layout`/`share` props to match based on kind of trigger and Transition Type. ```js <ViewTransition enter={{ 'navigation-back': 'enter-right', 'navigation-forward': 'enter-left', }} exit={{ 'navigation-back': 'exit-right', 'navigation-forward': 'exit-left', }}> ``` ## Events In addition, you can also observe the types in the View Transition Event callbacks as the second argument. That way you can pick different imperative Animations based on the cause. ```js <ViewTransition onUpdate={(inst, types) => { if (types.includes('navigation-back')) { ... } else if (types.includes('navigation-forward')) { ... } else { ... } }}> ``` ## Future In the future we might expose types to `useEffect` for more general purpose usage. This would also allow non-View Transition based Animations such as existing libraries to use this same feature to coordinate the same concept. We might also allow richer objects to be passed along here. Only the strings would apply to View Transitions but the imperative code and effects could do something else with them. DiffTrain build for [028c8e6](028c8e6)
This adds an isomorphic API to add Transition Types, which represent the cause, to the current Transition. This is currently mainly for View Transitions but as a concept it's broader and we might expand it to more features and object types in the future. ```js import { unstable_addTransitionType as addTransitionType } from 'react'; startTransition(() => { addTransitionType('my-transition-type'); setState(...); }); ``` If multiple transitions get entangled this is additive and all Transition Types are collected. You can also add more than one type to a Transition (hence the `add` prefix). Transition Types are reset after each commit. Meaning that `<Suspense>` revealing after a `startTransition` does not get any View Transition types associated with it. Note that the scoping rules for this is a little "wrong" in this implementation. Ideally it would be scoped to the nearest outer `startTransition` and grouped with any `setState` inside of it. Including Actions. However, since we currently don't have AsyncContext on the client, it would be too easy to drop a Transition Type if there were no other `setState` in the same `await` task. Multiple Transitions are entangled together anyway right now as a result. So this just tracks a global of all pending Transition Types for the next Transition. An inherent tricky bit with this API is that you could update multiple roots. In that case it should ideally be associated with each root. Transition Tracing solves this by associating a Transition with any updates that are later collected but this suffers from the problem mentioned above. Therefore, I just associate Transition Types with one root - the first one to commit. Since the View Transitions across roots are sequential anyway it kind of makes sense that only one really is the cause and the other one is subsequent. Transition Types can be used to apply different animations based on what caused the Transition. You have three different ways to choose from for how to use them: ## CSS It integrates with [View Transition Types](https://www.w3.org/TR/css-view-transitions-2/#active-view-transition-pseudo-examples) so you can match different animations based on CSS scopes: ```css :root:active-view-transition-type(my-transition-type) { &::view-transition-...(...) { ... } } ``` This is kind of a PITA to write though and if you have a CSS library that provide View Transition Classes it's difficult to import those into these scopes. ## Class per Type This PR also adds an object-as-map form that can be passed to all `className` properties: ```js <ViewTransition className={{ 'my-navigation-type': 'hello', 'default': 'world', }}> ``` If multiple types match, then they're joined together. If no types match then the special `"default"` entry is used instead. If any type has the value `"none"` then that wins and the ViewTransition is disabled (not assigned a name). These can be combined with `enter`/`exit`/`update`/`layout`/`share` props to match based on kind of trigger and Transition Type. ```js <ViewTransition enter={{ 'navigation-back': 'enter-right', 'navigation-forward': 'enter-left', }} exit={{ 'navigation-back': 'exit-right', 'navigation-forward': 'exit-left', }}> ``` ## Events In addition, you can also observe the types in the View Transition Event callbacks as the second argument. That way you can pick different imperative Animations based on the cause. ```js <ViewTransition onUpdate={(inst, types) => { if (types.includes('navigation-back')) { ... } else if (types.includes('navigation-forward')) { ... } else { ... } }}> ``` ## Future In the future we might expose types to `useEffect` for more general purpose usage. This would also allow non-View Transition based Animations such as existing libraries to use this same feature to coordinate the same concept. We might also allow richer objects to be passed along here. Only the strings would apply to View Transitions but the imperative code and effects could do something else with them. DiffTrain build for [028c8e6](facebook@028c8e6)
This adds an isomorphic API to add Transition Types, which represent the cause, to the current Transition. This is currently mainly for View Transitions but as a concept it's broader and we might expand it to more features and object types in the future. ```js import { unstable_addTransitionType as addTransitionType } from 'react'; startTransition(() => { addTransitionType('my-transition-type'); setState(...); }); ``` If multiple transitions get entangled this is additive and all Transition Types are collected. You can also add more than one type to a Transition (hence the `add` prefix). Transition Types are reset after each commit. Meaning that `<Suspense>` revealing after a `startTransition` does not get any View Transition types associated with it. Note that the scoping rules for this is a little "wrong" in this implementation. Ideally it would be scoped to the nearest outer `startTransition` and grouped with any `setState` inside of it. Including Actions. However, since we currently don't have AsyncContext on the client, it would be too easy to drop a Transition Type if there were no other `setState` in the same `await` task. Multiple Transitions are entangled together anyway right now as a result. So this just tracks a global of all pending Transition Types for the next Transition. An inherent tricky bit with this API is that you could update multiple roots. In that case it should ideally be associated with each root. Transition Tracing solves this by associating a Transition with any updates that are later collected but this suffers from the problem mentioned above. Therefore, I just associate Transition Types with one root - the first one to commit. Since the View Transitions across roots are sequential anyway it kind of makes sense that only one really is the cause and the other one is subsequent. Transition Types can be used to apply different animations based on what caused the Transition. You have three different ways to choose from for how to use them: ## CSS It integrates with [View Transition Types](https://www.w3.org/TR/css-view-transitions-2/#active-view-transition-pseudo-examples) so you can match different animations based on CSS scopes: ```css :root:active-view-transition-type(my-transition-type) { &::view-transition-...(...) { ... } } ``` This is kind of a PITA to write though and if you have a CSS library that provide View Transition Classes it's difficult to import those into these scopes. ## Class per Type This PR also adds an object-as-map form that can be passed to all `className` properties: ```js <ViewTransition className={{ 'my-navigation-type': 'hello', 'default': 'world', }}> ``` If multiple types match, then they're joined together. If no types match then the special `"default"` entry is used instead. If any type has the value `"none"` then that wins and the ViewTransition is disabled (not assigned a name). These can be combined with `enter`/`exit`/`update`/`layout`/`share` props to match based on kind of trigger and Transition Type. ```js <ViewTransition enter={{ 'navigation-back': 'enter-right', 'navigation-forward': 'enter-left', }} exit={{ 'navigation-back': 'exit-right', 'navigation-forward': 'exit-left', }}> ``` ## Events In addition, you can also observe the types in the View Transition Event callbacks as the second argument. That way you can pick different imperative Animations based on the cause. ```js <ViewTransition onUpdate={(inst, types) => { if (types.includes('navigation-back')) { ... } else if (types.includes('navigation-forward')) { ... } else { ... } }}> ``` ## Future In the future we might expose types to `useEffect` for more general purpose usage. This would also allow non-View Transition based Animations such as existing libraries to use this same feature to coordinate the same concept. We might also allow richer objects to be passed along here. Only the strings would apply to View Transitions but the imperative code and effects could do something else with them. DiffTrain build for [028c8e6](facebook@028c8e6)
This adds an isomorphic API to add Transition Types, which represent the cause, to the current Transition. This is currently mainly for View Transitions but as a concept it's broader and we might expand it to more features and object types in the future.
If multiple transitions get entangled this is additive and all Transition Types are collected. You can also add more than one type to a Transition (hence the
add
prefix).Transition Types are reset after each commit. Meaning that
<Suspense>
revealing after astartTransition
does not get any View Transition types associated with it.Note that the scoping rules for this is a little "wrong" in this implementation. Ideally it would be scoped to the nearest outer
startTransition
and grouped with anysetState
inside of it. Including Actions. However, since we currently don't have AsyncContext on the client, it would be too easy to drop a Transition Type if there were no othersetState
in the sameawait
task. Multiple Transitions are entangled together anyway right now as a result. So this just tracks a global of all pending Transition Types for the next Transition. An inherent tricky bit with this API is that you could update multiple roots. In that case it should ideally be associated with each root. Transition Tracing solves this by associating a Transition with any updates that are later collected but this suffers from the problem mentioned above. Therefore, I just associate Transition Types with one root - the first one to commit. Since the View Transitions across roots are sequential anyway it kind of makes sense that only one really is the cause and the other one is subsequent.Transition Types can be used to apply different animations based on what caused the Transition. You have three different ways to choose from for how to use them:
CSS
It integrates with View Transition Types so you can match different animations based on CSS scopes:
This is kind of a PITA to write though and if you have a CSS library that provide View Transition Classes it's difficult to import those into these scopes.
Class per Type
This PR also adds an object-as-map form that can be passed to all
className
properties:If multiple types match, then they're joined together. If no types match then the special
"default"
entry is used instead. If any type has the value"none"
then that wins and the ViewTransition is disabled (not assigned a name).These can be combined with
enter
/exit
/update
/layout
/share
props to match based on kind of trigger and Transition Type.Events
In addition, you can also observe the types in the View Transition Event callbacks as the second argument. That way you can pick different imperative Animations based on the cause.
Future
In the future we might expose types to
useEffect
for more general purpose usage. This would also allow non-View Transition based Animations such as existing libraries to use this same feature to coordinate the same concept.We might also allow richer objects to be passed along here. Only the strings would apply to View Transitions but the imperative code and effects could do something else with them.