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

feat: React 19 support #2349

Draft
wants to merge 23 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2843b31
feat!: React 19 support
CodyJasonBennett Jan 2, 2025
8f36899
fix: remaining type errors
CodyJasonBennett Jan 2, 2025
4053dcc
chore: cleanup
CodyJasonBennett Jan 2, 2025
a3bd26d
docs: add missing react-use-measure dep
CodyJasonBennett Jan 2, 2025
a0652aa
chore: resolve conflicts
CodyJasonBennett Jan 8, 2025
f1b2617
docs: add missing use-gesture dep
CodyJasonBennett Jan 8, 2025
6cdab5e
chore: prefer JSX.IntrinsicElements for backwards compat
CodyJasonBennett Jan 8, 2025
a7fd0ac
chore: keep PropsWithRef for React 18
CodyJasonBennett Jan 8, 2025
f201223
chore(SpringContext): add React 18 backwards compat
CodyJasonBennett Jan 8, 2025
f25225d
fix(SpringContext): swap mutation target
CodyJasonBennett Jan 8, 2025
f3cef19
fix(SpringContext): typo
CodyJasonBennett Jan 8, 2025
9b12ea6
fix(SpringContext): feature check renderable context for consumer
CodyJasonBennett Jan 9, 2025
cafa5b8
chore: cleanup
CodyJasonBennett Jan 9, 2025
97b33db
experiment: revert dep upgrade
CodyJasonBennett Jan 9, 2025
20c7df5
experiment: restore dep upgrade
CodyJasonBennett Jan 9, 2025
6c06950
experiment: keep MutableRefObject
CodyJasonBennett Jan 10, 2025
33364f8
experiment: revert deps upgrade
CodyJasonBennett Jan 10, 2025
51312c3
Revert "experiment: keep MutableRefObject"
CodyJasonBennett Jan 10, 2025
59579e2
fix: add annotation for 18.3 immutable useRef
CodyJasonBennett Jan 10, 2025
a61d9e7
experiment: keep MutableRefObject
CodyJasonBennett Jan 10, 2025
983ca3f
experiment: upgrade deps to React 19
CodyJasonBennett Jan 10, 2025
c30d8a0
chore: upgrade to rc.2
CodyJasonBennett Jan 10, 2025
efda668
chore: cleanup
CodyJasonBennett Jan 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified .yarn/releases/yarn-3.8.7.cjs
100755 → 100644
Empty file.
2 changes: 2 additions & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@remix-run/serve": "2.15.2",
"@remix-run/server-runtime": "2.15.2",
"@supabase/supabase-js": "2.47.10",
"@use-gesture/react": "^10.3.1",
"@vanilla-extract/css": "1.17.0",
"@vanilla-extract/dynamic": "2.1.2",
"@vanilla-extract/recipes": "0.5.5",
Expand All @@ -44,6 +45,7 @@
"react": "18.3.1",
"react-dom": "18.3.1",
"react-select": "5.9.0",
"react-use-measure": "^2.1.1",
"zod": "3.24.1"
},
"devDependencies": {
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"@changesets/cli": "2.27.11",
"@commitlint/cli": "19.6.1",
"@commitlint/config-conventional": "19.6.0",
"@react-three/fiber": "8.17.10",
"@react-three/fiber": "9.0.0-rc.2",
"@remix-run/dev": "2.15.2",
"@simonsmith/cypress-image-snapshot": "9.1.0",
"@swc/core": "1.10.4",
Expand All @@ -79,8 +79,8 @@
"@types/jest": "29.5.14",
"@types/lodash.clamp": "4.0.9",
"@types/lodash.shuffle": "4.2.9",
"@types/react": "18.3.18",
"@types/react-dom": "18.3.5",
"@types/react": "19.0.2",
"@types/react-dom": "19.0.2",
"@types/react-lazyload": "3.2.3",
"@types/react-native": "0.73.0",
"@types/styled-components": "5.1.34",
Expand All @@ -95,8 +95,8 @@
"mock-raf": "npm:@react-spring/[email protected]",
"prettier": "3.4.2",
"pretty-quick": "4.0.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-konva": "18.2.10",
"react-native": "0.76.5",
"react-zdog": "1.2.2",
Expand Down
14 changes: 12 additions & 2 deletions packages/animated/src/withAnimated.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import * as React from 'react'
import { forwardRef, useRef, Ref, useCallback, useEffect } from 'react'
import {
forwardRef,
useRef,
Ref,
useCallback,
useEffect,
MutableRefObject,
} from 'react'
import {
is,
each,
Expand Down Expand Up @@ -66,7 +73,10 @@ export const withAnimated = (Component: any, host: HostConfig) => {

const observer = new PropsObserver(callback, deps)

const observerRef = useRef<PropsObserver>()
// NOTE: useRef is bugged as immutable in 18.3 types
const observerRef = useRef<PropsObserver>(
null
) as MutableRefObject<PropsObserver | null>
useIsomorphicLayoutEffect(() => {
observerRef.current = observer

Expand Down
65 changes: 43 additions & 22 deletions packages/core/src/SpringContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,54 @@ export interface SpringContext {
immediate?: boolean
}

export const SpringContext = ({
children,
...props
}: PropsWithChildren<SpringContext>) => {
const inherited = useContext(ctx)
export const SpringContext = makeRenderableContext<
SpringContext,
PropsWithChildren<SpringContext>
>(
Context =>
({ children, ...props }) => {
const inherited = useContext(Context)

// Inherited values are dominant when truthy.
const pause = props.pause || !!inherited.pause,
immediate = props.immediate || !!inherited.immediate
// Inherited values are dominant when truthy.
const pause = props.pause || !!inherited.pause
const immediate = props.immediate || !!inherited.immediate

// Memoize the context to avoid unwanted renders.
props = useMemoOne(() => ({ pause, immediate }), [pause, immediate])
// Memoize the context to avoid unwanted renders.
props = useMemoOne(() => ({ pause, immediate }), [pause, immediate])

const { Provider } = ctx
return <Provider value={props}>{children}</Provider>
return <Context.Provider value={props}>{children}</Context.Provider>
},
{} as SpringContext
)

interface RenderableContext<T, P> extends React.ProviderExoticComponent<P> {
Provider: RenderableContext<T, P>
Consumer: React.Consumer<T>
displayName?: string
}

const ctx = makeContext(SpringContext, {} as SpringContext)
/** Make the `target` compatible with `useContext` */
function makeRenderableContext<T, P>(
target: (context: React.Context<T>) => React.FunctionComponent<P>,
init: T
): RenderableContext<T, P> {
let context = React.createContext(init)
context = Object.assign(target(context), context)

// https://github.com/facebook/react/pull/28226
if ('_context' in context.Provider) {
context.Provider._context = context
} else {
// @ts-ignore React 18 types disallow this
context.Provider = context
}

// Allow `useContext(SpringContext)` in TypeScript.
SpringContext.Provider = ctx.Provider
SpringContext.Consumer = ctx.Consumer
CodyJasonBennett marked this conversation as resolved.
Show resolved Hide resolved
if ('_context' in context.Consumer) {
context.Consumer._context = context
} else {
// @ts-expect-error
context.Consumer = context
}

/** Make the `target` compatible with `useContext` */
function makeContext<T>(target: any, init: T): React.Context<T> {
Object.assign(target, React.createContext(init))
target.Provider._context = target
target.Consumer._context = target
return target
return context as unknown as RenderableContext<T, P>
}
1 change: 1 addition & 0 deletions packages/core/src/components/Spring.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { JSX } from 'react'
import { NoInfer, UnknownProps } from '@react-spring/types'
import { useSpring, UseSpringProps } from '../hooks/useSpring'
import { SpringValues, SpringToFn, SpringChain } from '../types'
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/components/Transition.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { JSX } from 'react'
import { Valid } from '../types/common'
import { TransitionComponentProps } from '../types'
import { useTransition } from '../hooks'
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/hooks/useInView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function useInView<TElement extends HTMLElement>(
args?: IntersectionArgs
) {
const [isInView, setIsInView] = useState(false)
const ref = useRef<TElement>()
const ref = useRef<TElement>(null)

const propsFn = is.fun(props) && props

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/hooks/useSpring.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ interface TestContext extends SpringContext {
}

function createUpdater(Component: React.ComponentType<{ args: [any, any?] }>) {
let prevElem: JSX.Element | undefined
let prevElem: React.JSX.Element | undefined
let result: RenderResult | undefined

const context: TestContext = {
Expand All @@ -139,7 +139,7 @@ function createUpdater(Component: React.ComponentType<{ args: [any, any?] }>) {
}
})

function renderWithContext(elem: JSX.Element) {
function renderWithContext(elem: React.JSX.Element) {
const wrapped = <SpringContext {...context}>{elem}</SpringContext>
if (result) result.rerender(wrapped)
else result = render(wrapped)
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/types/transition.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ReactNode } from 'react'
import { ReactNode, JSX } from 'react'
import {
Lookup,
ObjectFromUnion,
Expand Down
6 changes: 3 additions & 3 deletions packages/parallax/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export const ParallaxLayer = React.memo(

React.useImperativeHandle(ref, () => layer)

const layerRef = useRef<any>()
const layerRef = useRef<any>(null)

const setSticky = (height: number, scrollTop: number) => {
const start = layer.sticky!.start! * height
Expand Down Expand Up @@ -229,8 +229,8 @@ export const Parallax = React.memo(
...rest
} = props

const containerRef = useRef<any>()
const contentRef = useRef<any>()
const containerRef = useRef<any>(null)
const contentRef = useRef<any>(null)

const state: IParallax = useMemoOne(
() => ({
Expand Down
5 changes: 3 additions & 2 deletions packages/shared/src/hooks/useMemoOne.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react'
import { useEffect, useRef, useState, MutableRefObject } from 'react'

type Cache<T> = {
inputs?: any[]
Expand All @@ -14,7 +14,8 @@ export function useMemoOne<T>(getResult: () => T, inputs?: any[]): T {
})
)

const committed = useRef<Cache<T>>()
// NOTE: useRef is bugged as immutable in 18.3 types
const committed = useRef<Cache<T>>(null) as MutableRefObject<Cache<T> | null>
const prevCache = committed.current

let cache = prevCache
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/hooks/usePrev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react'

/** Use a value from the previous render */
export function usePrev<T>(value: T): T | undefined {
const prevRef = useRef<any>()
const prevRef = useRef<any>(null)
useEffect(() => {
prevRef.current = value
})
Expand Down
2 changes: 1 addition & 1 deletion targets/three/src/animated.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { CSSProperties, ForwardRefExoticComponent, FC } from 'react'
import { CSSProperties, ForwardRefExoticComponent, FC, JSX } from 'react'
import {
AssignableKeys,
ComponentPropsWithRef,
Expand Down
2 changes: 1 addition & 1 deletion targets/three/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ addEffect(() => {
})

const host = createHost(primitives, {
// @ts-expect-error r3f related
// @ts-ignore related to R3F v8
applyAnimatedValues: applyProps,
})

Expand Down
2 changes: 1 addition & 1 deletion targets/three/src/primitives.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as THREE from 'three'
import '@react-three/fiber'
import { JSX } from 'react'

export type Primitives = keyof JSX.IntrinsicElements

Expand Down
2 changes: 2 additions & 0 deletions targets/web/src/primitives.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { JSX } from 'react'

export type Primitives = keyof JSX.IntrinsicElements
export const primitives: Primitives[] = [
'a',
Expand Down
Loading
Loading