react-adaptive-hooks

by GoogleChromeLabs

GoogleChromeLabs / react-adaptive-hooks

Deliver experiences best suited to a user's device and network constraints

4.4K Stars 100 Forks Last release: Not found Apache License 2.0 67 Commits 9 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

React Adaptive Loading Hooks & Utilities · Build Status npm bundle size

Deliver experiences best suited to a user's device and network constraints (experimental)

This is a suite of React Hooks and utilities for adaptive loading based on a user's:

It can be used to add patterns for adaptive resource loading, data-fetching, code-splitting and capability toggling.

Objective

Make it easier to target low-end devices while progressively adding high-end-only features on top. Using these hooks and utilities can help you give users a great experience best suited to their device and network constraints.

Installation

npm i react-adaptive-hooks --save
or
yarn add react-adaptive-hooks

Usage

You can import the hooks you wish to use as follows:

import { useNetworkStatus } from 'react-adaptive-hooks/network';
import { useSaveData } from 'react-adaptive-hooks/save-data';
import { useHardwareConcurrency } from 'react-adaptive-hooks/hardware-concurrency';
import { useMemoryStatus } from 'react-adaptive-hooks/memory';
import { useMediaCapabilitiesDecodingInfo } from 'react-adaptive-hooks/media-capabilities';

and then use them in your components. Examples for each hook and utility can be found below:

Network

useNetworkStatus
React hook for adapting based on network status (effective connection type)
import React from 'react';

import { useNetworkStatus } from 'react-adaptive-hooks/network';

const MyComponent = () => { const { effectiveConnectionType } = useNetworkStatus();

let media; switch(effectiveConnectionType) { case 'slow-2g': media = low resolution; break; case '2g': media = medium resolution; break; case '3g': media = high resolution; break; case '4g': media = ; break; default: media = ; break; }

return

{media}
; };

effectiveConnectionType
values can be
slow-2g
,
2g
,
3g
, or
4g
.

This hook accepts an optional

initialEffectiveConnectionType
string argument, which can be used to provide a
effectiveConnectionType
state value when the user's browser does not support the relevant NetworkInformation API. Passing an initial value can also prove useful for server-side rendering, where the developer can pass an ECT Client Hint to detect the effective network connection type.
// Inside of a functional React component
const initialEffectiveConnectionType = '4g';
const { effectiveConnectionType } = useNetworkStatus(initialEffectiveConnectionType);

Save Data

useSaveData
utility for adapting based on the user's browser Data Saver preferences.
import React from 'react';

import { useSaveData } from 'react-adaptive-hooks/save-data';

const MyComponent = () => { const { saveData } = useSaveData(); return (

{ saveData ? : }
); };

saveData
values can be
true
or
false
.

This hook accepts an optional

initialSaveData
boolean argument, which can be used to provide a
saveData
state value when the user's browser does not support the relevant NetworkInformation API. Passing an initial value can also prove useful for server-side rendering, where the developer can pass a server Save-Data Client Hint that has been converted to a boolean to detect the user's data saving preference.
// Inside of a functional React component
const initialSaveData = true;
const { saveData } = useSaveData(initialSaveData);

CPU Cores / Hardware Concurrency

useHardwareConcurrency
utility for adapting to the number of logical CPU processor cores on the user's device.
import React from 'react';

import { useHardwareConcurrency } from 'react-adaptive-hooks/hardware-concurrency';

const MyComponent = () => { const { numberOfLogicalProcessors } = useHardwareConcurrency(); return (

{ numberOfLogicalProcessors <= 4 ? : }
); };

numberOfLogicalProcessors
values can be the number of logical processors available to run threads on the user's device.

Memory

useMemoryStatus
utility for adapting based on the user's device memory (RAM)
import React from 'react';

import { useMemoryStatus } from 'react-adaptive-hooks/memory';

const MyComponent = () => { const { deviceMemory } = useMemoryStatus(); return (

{ deviceMemory < 4 ? : }
); };

deviceMemory
values can be the approximate amount of device memory in gigabytes.

This hook accepts an optional

initialMemoryStatus
object argument, which can be used to provide a
deviceMemory
state value when the user's browser does not support the relevant DeviceMemory API. Passing an initial value can also prove useful for server-side rendering, where the developer can pass a server Device-Memory Client Hint to detect the memory capacity of the user's device.
// Inside of a functional React component
const initialMemoryStatus = { deviceMemory: 4 };
const { deviceMemory } = useMemoryStatus(initialMemoryStatus);

Media Capabilities

useMediaCapabilitiesDecodingInfo
utility for adapting based on the user's device media capabilities.

Use case: this hook can be used to check if we can play a certain content type. For example, Safari does not support WebM so we want to fallback to MP4 but if Safari at some point does support WebM it will automatically load WebM videos.

import React from 'react';

import { useMediaCapabilitiesDecodingInfo } from 'react-adaptive-hooks/media-capabilities';

const webmMediaDecodingConfig = { type: 'file', // 'record', 'transmission', or 'media-source' video: { contentType: 'video/webm;codecs=vp8', // valid content type width: 800, // width of the video height: 600, // height of the video bitrate: 10000, // number of bits used to encode 1s of video framerate: 30 // number of frames making up that 1s. } };

const initialMediaCapabilitiesInfo = { powerEfficient: true };

const MyComponent = ({ videoSources }) => { const { mediaCapabilitiesInfo } = useMediaCapabilitiesDecodingInfo(webmMediaDecodingConfig, initialMediaCapabilitiesInfo);

return (

); };

mediaCapabilitiesInfo
value contains the three Boolean properties supported, smooth, and powerEfficient, which describe whether decoding the media described would be supported, smooth, and powerEfficient.

This utility accepts a MediaDecodingConfiguration object argument and an optional

initialMediaCapabilitiesInfo
object argument, which can be used to provide a
mediaCapabilitiesInfo
state value when the user's browser does not support the relevant Media Capabilities API or no media configuration was given.

Adaptive Code-loading & Code-splitting

Code-loading

Deliver a light, interactive core experience to users and progressively add high-end-only features on top, if a user's hardware can handle it. Below is an example using the Network Status hook:

import React, { Suspense, lazy } from 'react';

import { useNetworkStatus } from 'react-adaptive-hooks/network';

const Full = lazy(() => import(/* webpackChunkName: "full" / './Full.js')); const Light = lazy(() => import(/ webpackChunkName: "light" */ './Light.js'));

const MyComponent = () => { const { effectiveConnectionType } = useNetworkStatus(); return (

Loading...

}> { effectiveConnectionType === '4g' ? : }

); };

export default MyComponent;

Light.js: ```js import React from 'react';

const Light = ({ imageUrl, ...rest }) => ( );

export default Light; ```

Full.js: ```js import React from 'react'; import Magnifier from 'react-magnifier';

const Full = ({ imageUrl, ...rest }) => ( );

export default Full; ```

Code-splitting

We can extend

React.lazy()
by incorporating a check for a device or network signal. Below is an example of network-aware code-splitting. This allows us to conditionally load a light core experience or full-fat experience depending on the user's effective connection speed (via
navigator.connection.effectiveType
).
import React, { Suspense } from 'react';

const Component = React.lazy(() => { const effectiveType = navigator.connection ? navigator.connection.effectiveType : null

let module; switch (effectiveType) { case '3g': module = import(/* webpackChunkName: "light" / './Light.js'); break; case '4g': module = import(/ webpackChunkName: "full" / './Full.js'); break; default: module = import(/ webpackChunkName: "full" */ './Full.js'); break; }

return module; });

const App = () => { return (

Loading...

}>

); };

export default App;

Server-side rendering support

The built version of this package uses ESM (native JS modules) by default, but is not supported on the server-side. When using this package in a web framework like Next.js with server-rendering, we recommend you

  • Transpile the package by installing next-transpile-modules. (example project). This is because Next.js currently does not pass

    node_modules
    into webpack server-side.
  • Use a UMD build as in the following code-snippet: (example project)

    import {
    useNetworkStatus,
    useSaveData,
    useHardwareConcurrency,
    useMemoryStatus,
    useMediaCapabilitiesDecodingInfo
    } from 'react-adaptive-hooks/dist/index.umd.js';
    

Browser Support

Demos

Network

Save Data

CPU Cores / Hardware Concurrency

Memory

Hybrid

References

License

Licensed under the Apache-2.0 license.

Team

This project is brought to you by Addy Osmani and Anton Karlovskiy.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.