DEV Community

Cover image for React.js ~New feature in React 19.3~
Ogasawara Kakeru
Ogasawara Kakeru

Posted on

React.js ~New feature in React 19.3~

Let's dive into new features in React.js 19.3

1.View Transitions

Basic usage

View Transitions is the <ViewTransition> component that Animates Adding and Removing Elements, transition, and sizing.

import { ViewTransition, useState, startTransition } from 'react';

export default function Component() {
  const [showItem, setShowItem] = useState(false);

  return (
    <>
      <button
        onClick={() => {
          startTransition(() => {
            setShowItem((prev) => !prev);
          });
        }}>
        {showItem ? '➖' : '➕'}
      </button>

      {showItem && (
        <ViewTransition>
          <Video video={videos[0]} />
        </ViewTransition>
      )}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Example of defining the type of the transition

Different animations (e.g. enter, exit, update, share) are applied automatically.

Case that multiple animations are needed:

import { ViewTransition, addTransitionType, useState, startTransition } from 'react';

export default function Carousel() {
  const [selected, setSelected] = useState(0);

  return (
    <>
      <button
        onClick={() => {
          startTransition(() => {
            addTransitionType('previous');
            setSelected(c => c > 0 ? c - 1 : videos.length - 1);
          });
        }}>
        ⬅️
      </button>

      <button
        onClick={() => {
          startTransition(() => {
            addTransitionType('next');
            setSelected(c => c + 1 < videos.length ? c + 1 : 0);
          });
        }}>
        ➡️
      </button>

      <ViewTransition
        enter={{
          'next': 'from-right',
          'previous': 'from-left'
        }}
        exit={{
          'next': 'to-left',
          'previous': 'to-right'
        }}
      >
        <Video video={videos[selected]} />
      </ViewTransition>
    </>
  );
}

Enter fullscreen mode Exit fullscreen mode

Combination with Suspense

You can animate the transition from the fallback to the final content by wrapping boundary of Suspense with View Transition

import { Suspense, useState, startTransition, use, ViewTransition } from 'react';

export default function Component() {
  const [showItem, setShowItem] = useState(false);

  return (
    <>
      <button
        onClick={() => {
          startTransition(() => {
            setShowItem((prev) => !prev);
          });
        }}>
        {showItem ? '➖' : '➕'}
      </button>

      {showItem && (
        <ViewTransition update="auto" default="none">
          <Suspense fallback={<VideoPlaceholder />}>
            <LazyVideo />
          </Suspense>
        </ViewTransition>
      )}
    </>
  );
}

function LazyVideo() {
  const video = use(fetchVideo());
  return <Video video={video} />;
}

Enter fullscreen mode Exit fullscreen mode

2.Fragment Refs

Basic usage

Using Fragment Refs enables low-level control over DOM nodes within a group of sibling elements that do not share a single parent DOM element.

import { Fragment, useRef, useEffect } from 'react';

function Component() {
  const fragmentRef = useRef(null);

  useEffect(() => {
    const fragmentInstance = fragmentRef.current;
    fragmentInstance.focus(); // Move the focus
  }, []);

  return (
    <Fragment ref={fragmentRef}>
      {posts.map(post => (
        <Heading key={post.id}>
          {post.title}
        </Heading>
      ))}
    </Fragment>
  );
}
Enter fullscreen mode Exit fullscreen mode

An example of Usage

//InView Component
import { Fragment, useRef, useEffect } from 'react';

function InView({ onChange, children }) {
  const fragmentRef = useRef(null);

  useEffect(() => {
    const fragmentInstance = fragmentRef.current;
    const observer = new IntersectionObserver(([entry]) => {
      onChange(entry.isIntersecting);
    });

    fragmentInstance.observeUsing(observer);

    return () => {
      fragmentInstance.unobserveUsing(observer);
    };
  }, [onChange]);

  return <Fragment ref={fragmentRef}>{children}</Fragment>;
}

export default function App() {
  const [isVisible, setIsVisible] = useState(true);

  return (
    <div>
      <div>Scroll down</div>

      <InView onChange={setIsVisible}>
        <Card title="First section" />
        <Card title="Second section" />
      </InView>

      <div>Scroll up</div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

3.Browser API

Basic usage

Calling use(browser()) enables to exclude the component that depends on browser API from SSR.

import { Suspense, use } from 'react';
import { browser } from 'react-dom';

function TimeZone() {
  use(browser()); // This component becomes Suspense on the server, while runs normally on the client.

  const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;

  return <p>{timeZone}</p>;
}

export default function App() {
  return (
    <>
      <p>Your current time zone is:</p>
      <Suspense fallback="Loading...">
        <TimeZone />
      </Suspense>
    </>
  );
}

Enter fullscreen mode Exit fullscreen mode

Use under certain condition

function TimeZone({ defaultValue }) {
  if (defaultValue) {
    return <p>{defaultValue}</p>; // If defaultValue exists, returns `p` tag
  }

  use(browser()); // Otherwise, executes the browser API.
  const localTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;

  return <p>{localTimeZone}</p>;
}

export default function App() {
  return (
    <>
      <div>
        <p>The event's time zone is:</p>
        <TimeZone defaultValue="America/New_York" /> {/* Rendering on the server */}
      </div>

      <hr />

      <div>
        <p>Your current time zone is:</p>
        <Suspense fallback="Loading...">
          <TimeZone /> {/* Running on the client */}
        </Suspense>
      </div>
    </>
  );
}

Enter fullscreen mode Exit fullscreen mode

Use of useQuery

function useBrowserQuery(query, options) {
  if (options.initialData === undefined) {
    use(browser()); // If initialData doesn't exist, executes on browser.
  }

  return useQuery(query, options);
}

function ProductDetails({ productId, initialData }) {
  const product = useBrowserQuery(`/api/products/${productId}`, {
    initialData,
  });

  return <h1>{product.name}</h1>;
}

Enter fullscreen mode Exit fullscreen mode

4.React Server Components - Improving Context

Before improving(Need wrapper component)

// user-context.js
'use client';
import { createContext } from 'react';

export const UserContext = createContext(null);

export function UserProvider({ currentUser, children }) {
  return <UserContext value={currentUser}>{children}</UserContext>;
}

// server-component.js
import { UserProvider } from './user-context';

export async function Layout({ children }) {
  const currentUser = await getCurrentUser();

  return (
    <UserProvider currentUser={currentUser}>
      {children}
    </UserProvider>
  );
}

Enter fullscreen mode Exit fullscreen mode

After improving(Render Context derectly)

You can render Context from 'use client' module on the server component directly without wrapper component(Provider).

// user-context.js
'use client';
import { createContext } from 'react';

export const UserContext = createContext(null);

// server-component.js
import { UserContext } from './user-context';

export async function Layout({ children }) {
  const currentUser = await getCurrentUser();

  return (
    <UserContext value={currentUser}>
      {children}
    </UserContext>
  );
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)