Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-adaptive-hooks

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-adaptive-hooks - npm Package Compare versions

Comparing version 0.0.5 to 0.0.6

dist/index.js

27

hardware-concurrency/hardware-concurrency.test.js

@@ -25,6 +25,29 @@ /*

describe('useHardwareConcurrency', () => {
const navigator = window.navigator;
afterEach(() => {
if (!window.navigator) window.navigator = navigator;
});
test(`should return "true" for unsupported case`, () => {
Object.defineProperty(window, 'navigator', {
value: undefined,
configurable: true,
writable: true
});
const { useHardwareConcurrency } = require('./');
const { result } = renderHook(() => useHardwareConcurrency());
expect(result.current.unsupported).toBe(true);
});
test(`should return window.navigator.hardwareConcurrency`, () => {
const { useHardwareConcurrency } = require('./');
const { result } = renderHook(() => useHardwareConcurrency());
expect(result.current.numberOfLogicalProcessors).toBe(window.navigator.hardwareConcurrency);
expect(result.current.numberOfLogicalProcessors).toBe(
window.navigator.hardwareConcurrency
);
expect(result.current.unsupported).toBe(false);
});

@@ -42,2 +65,3 @@

expect(result.current.numberOfLogicalProcessors).toEqual(4);
expect(result.current.unsupported).toBe(false);
});

@@ -55,3 +79,4 @@

expect(result.current.numberOfLogicalProcessors).toEqual(2);
expect(result.current.unsupported).toBe(false);
});
});

5

hardware-concurrency/index.js

@@ -19,3 +19,6 @@ /*

if (typeof navigator !== 'undefined' && 'hardwareConcurrency' in navigator) {
initialHardwareConcurrency = { numberOfLogicalProcessors: navigator.hardwareConcurrency };
initialHardwareConcurrency = {
unsupported: false,
numberOfLogicalProcessors: navigator.hardwareConcurrency
};
} else {

@@ -22,0 +25,0 @@ initialHardwareConcurrency = { unsupported: true };

@@ -1,4 +0,4 @@

export { default as useNetworkStatus } from './network';
export { default as useSaveData } from './save-data';
export { default as useMemoryStatus } from './memory';
export { default as useHardwareConcurrency } from './hardware-concurrency';
export { useNetworkStatus } from './network';
export { useSaveData } from './save-data';
export { useMemoryStatus } from './memory';
export { useHardwareConcurrency } from './hardware-concurrency';

@@ -23,19 +23,26 @@ /*

}
let initialMemoryStatus;
let memoryStatus;
if (!unsupported) {
const performanceMemory = 'memory' in performance ? performance.memory : null;
initialMemoryStatus = {
memoryStatus = {
unsupported,
deviceMemory: navigator.deviceMemory,
totalJSHeapSize: performanceMemory ? performanceMemory.totalJSHeapSize : null,
totalJSHeapSize: performanceMemory
? performanceMemory.totalJSHeapSize
: null,
usedJSHeapSize: performanceMemory ? performanceMemory.usedJSHeapSize : null,
jsHeapSizeLimit: performanceMemory ? performanceMemory.jsHeapSizeLimit : null
jsHeapSizeLimit: performanceMemory
? performanceMemory.jsHeapSizeLimit
: null
};
} else {
initialMemoryStatus = { unsupported };
memoryStatus = { unsupported };
}
const useMemoryStatus = () => {
return { ...initialMemoryStatus };
const useMemoryStatus = initialMemoryStatus => {
return unsupported && initialMemoryStatus
? { ...memoryStatus, ...initialMemoryStatus }
: { ...memoryStatus };
};
export { useMemoryStatus };

@@ -25,2 +25,3 @@ /*

const getMemoryStatus = currentResult => ({
unsupported: false,
deviceMemory: currentResult.deviceMemory,

@@ -40,2 +41,17 @@ totalJSHeapSize: currentResult.totalJSHeapSize,

test('should return initialMemoryStatus for unsupported case', () => {
const mockInitialMemoryStatus = {
deviceMemory: 4
};
const { deviceMemory } = mockInitialMemoryStatus;
const { useMemoryStatus } = require('./');
const { result } = renderHook(() =>
useMemoryStatus(mockInitialMemoryStatus)
);
expect(result.current.unsupported).toBe(true);
expect(result.current.deviceMemory).toEqual(deviceMemory);
});
test('should return mockMemory status', () => {

@@ -60,4 +76,52 @@ const mockMemoryStatus = {

expect(getMemoryStatus(result.current)).toEqual(mockMemoryStatus);
expect(getMemoryStatus(result.current)).toEqual({
...mockMemoryStatus,
unsupported: false
});
});
test('should return mockMemory status without performance memory data', () => {
const mockMemoryStatus = {
deviceMemory: 4
};
global.navigator.deviceMemory = mockMemoryStatus.deviceMemory;
delete global.window.performance.memory;
const { useMemoryStatus } = require('./');
const { result } = renderHook(() => useMemoryStatus());
expect(result.current.deviceMemory).toEqual(mockMemoryStatus.deviceMemory);
expect(result.current.unsupported).toEqual(false);
});
test('should not return initialMemoryStatus for supported case', () => {
const mockMemoryStatus = {
deviceMemory: 4,
totalJSHeapSize: 60,
usedJSHeapSize: 40,
jsHeapSizeLimit: 50
};
const mockInitialMemoryStatus = {
deviceMemory: 4
};
global.navigator.deviceMemory = mockMemoryStatus.deviceMemory;
global.window.performance.memory = {
totalJSHeapSize: mockMemoryStatus.totalJSHeapSize,
usedJSHeapSize: mockMemoryStatus.usedJSHeapSize,
jsHeapSizeLimit: mockMemoryStatus.jsHeapSizeLimit
};
const { useMemoryStatus } = require('./');
const { result } = renderHook(() =>
useMemoryStatus(mockInitialMemoryStatus)
);
expect(getMemoryStatus(result.current)).toEqual({
...mockMemoryStatus,
unsupported: false
});
});
});

@@ -21,3 +21,3 @@ /*

const useNetworkStatus = () => {
const useNetworkStatus = initialEffectiveConnectionType => {
if ('connection' in navigator && 'effectiveType' in navigator.connection) {

@@ -29,7 +29,8 @@ unsupported = false;

const initialNetworkStatus = !unsupported ? {
effectiveConnectionType: navigator.connection.effectiveType
} : {
unsupported
};
const initialNetworkStatus = {
unsupported,
effectiveConnectionType: unsupported
? initialEffectiveConnectionType
: navigator.connection.effectiveType
};

@@ -42,3 +43,5 @@ const [networkStatus, setNetworkStatus] = useState(initialNetworkStatus);

const updateECTStatus = () => {
setNetworkStatus({ effectiveConnectionType: navigatorConnection.effectiveType });
setNetworkStatus({
effectiveConnectionType: navigatorConnection.effectiveType
});
};

@@ -45,0 +48,0 @@ navigatorConnection.addEventListener('change', updateECTStatus);

@@ -22,19 +22,80 @@ /*

describe('useNetworkStatus', () => {
const map = {};
const ectStatusListeners = {
addEventListener: jest.fn().mockImplementation((event, callback) => {
map[event] = callback;
}),
removeEventListener: jest.fn()
};
afterEach(() => {
Object.values(ectStatusListeners).forEach(listener => listener.mockClear());
});
/**
* Tests that addEventListener or removeEventListener was called during the
* lifecycle of the useEffect hook within useNetworkStatus
*/
const testEctStatusEventListenerMethod = method => {
expect(method).toBeCalledTimes(1);
expect(method.mock.calls[0][0]).toEqual('change');
expect(method.mock.calls[0][1].constructor).toEqual(Function);
};
test(`should return "true" for unsupported case`, () => {
const { result } = renderHook(() => useNetworkStatus());
expect(result.current.unsupported).toBe(true);
});
test('should return initialEffectiveConnectionType for unsupported case', () => {
const initialEffectiveConnectionType = '4g';
const { result } = renderHook(() =>
useNetworkStatus(initialEffectiveConnectionType)
);
expect(result.current.unsupported).toBe(true);
expect(result.current.effectiveConnectionType).toBe(
initialEffectiveConnectionType
);
});
test('should return 4g of effectiveConnectionType', () => {
global.navigator.connection = {
effectiveType: '4g',
addEventListener: jest.fn(),
removeEventListener: jest.fn()
...ectStatusListeners,
effectiveType: '4g'
};
const { result } = renderHook(() => useNetworkStatus());
testEctStatusEventListenerMethod(ectStatusListeners.addEventListener);
expect(result.current.unsupported).toBe(false);
expect(result.current.effectiveConnectionType).toEqual('4g');
});
test('should not return initialEffectiveConnectionType for supported case', () => {
const initialEffectiveConnectionType = '2g';
global.navigator.connection = {
...ectStatusListeners,
effectiveType: '4g'
};
const { result } = renderHook(() =>
useNetworkStatus(initialEffectiveConnectionType)
);
testEctStatusEventListenerMethod(ectStatusListeners.addEventListener);
expect(result.current.unsupported).toBe(false);
expect(result.current.effectiveConnectionType).toEqual('4g');
});
test('should update the effectiveConnectionType state', () => {
const { result } = renderHook(() => useNetworkStatus());
act(() => result.current.setNetworkStatus({effectiveConnectionType: '2g'}));
act(() =>
result.current.setNetworkStatus({ effectiveConnectionType: '2g' })
);
expect(result.current.effectiveConnectionType).toEqual('2g');

@@ -44,9 +105,5 @@ });

test('should update the effectiveConnectionType state when navigator.connection change event', () => {
const map = {};
global.navigator.connection = {
effectiveType: '2g',
addEventListener: jest.fn().mockImplementation((event, callback) => {
map[event] = callback;
}),
removeEventListener: jest.fn()
...ectStatusListeners,
effectiveType: '2g'
};

@@ -60,2 +117,15 @@

});
test('should remove the listener for the navigator.connection change event on unmount', () => {
global.navigator.connection = {
...ectStatusListeners,
effectiveType: '2g'
};
const { unmount } = renderHook(() => useNetworkStatus());
testEctStatusEventListenerMethod(ectStatusListeners.addEventListener);
unmount();
testEctStatusEventListenerMethod(ectStatusListeners.removeEventListener);
});
});
{
"name": "react-adaptive-hooks",
"version": "0.0.5",
"version": "0.0.6",
"description": "Give users a great experience best suited to their device and network constraints",
"main": "index.js",
"repository": "https://github.com/GoogleChromeLabs/react-adaptive-hooks",

@@ -10,4 +9,12 @@ "author": "Google Chrome",

"private": false,
"main": "dist/index.js",
"module": "dist/index.mjs",
"unpkg": "dist/index.umd.js",
"source": "index.js",
"scripts": {
"test": "jest"
"build": "microbundle",
"dev": "microbundle watch",
"prepare": "npm run build",
"test": "jest",
"test:coverage": "jest --coverage"
},

@@ -18,11 +25,8 @@ "peerDependencies": {

"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/node": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@testing-library/react-hooks": "^1.1.0",
"jest": "^24.8.0",
"microbundle": "0.11.0",
"react": "16.9.0",
"react-dom": "16.9.0",
"ua-parser-js": "^0.7.20",
"@testing-library/react-hooks": "^1.1.0",
"babel-polyfill": "^6.26.0",
"jest": "^24.8.0",
"react-test-renderer": "16.9.0"

@@ -29,0 +33,0 @@ },

@@ -1,6 +0,6 @@

# React Adaptive Loading Hooks · ![](https://img.shields.io/github/license/GoogleChromeLabs/react-adaptive-hooks.svg) [![Build Status](https://travis-ci.org/GoogleChromeLabs/react-adaptive-hooks.svg?branch=master)](https://travis-ci.org/GoogleChromeLabs/react-adaptive-hooks) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-adaptive-hooks)
# React Adaptive Loading Hooks & Utilities · ![](https://img.shields.io/github/license/GoogleChromeLabs/react-adaptive-hooks.svg) [![Build Status](https://travis-ci.org/GoogleChromeLabs/react-adaptive-hooks.svg?branch=master)](https://travis-ci.org/GoogleChromeLabs/react-adaptive-hooks) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-adaptive-hooks)
> Deliver experiences best suited to a user's device and network constraints (experimental)
This is a suite of [React Hooks](https://reactjs.org/docs/hooks-overview.html) for adaptive loading based on a user's:
This is a suite of [React Hooks](https://reactjs.org/docs/hooks-overview.html) and utilities for adaptive loading based on a user's:

@@ -16,7 +16,7 @@ * [Network via effective connection type](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType)

Make it easier to target low-end devices while progressively adding high-end-only features on top. Using these hooks can help you give users a great experience best suited to their device and network constraints.
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`
`npm i react-adaptive-hooks --save` or `yarn add react-adaptive-hooks`

@@ -32,10 +32,9 @@ ## Usage

import { useMemoryStatus } from 'react-adaptive-hooks/memory';
```
and then use them in your components. Examples for each hook can be found below.
and then use them in your components. Examples for each hook and utility can be found below:
### Network
`useNetworkStatus` React hook for getting network status (effective connection type)
`useNetworkStatus` React hook for adapting based on network status (effective connection type)

@@ -73,5 +72,15 @@ ```js

`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](https://wicg.github.io/netinfo/). Passing an initial value can also prove useful for server-side rendering, where the developer can pass an [ECT Client Hint](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints#ect) to detect the effective network connection type.
```js
// Inside of a functional React component
const initialEffectiveConnectionType = '4g';
const { effectiveConnectionType } = useNetworkStatus(initialEffectiveConnectionType);
```
### Save Data
`useSaveData` React hook for getting Save Data whether it's Lite mode enabled or not
`useSaveData` utility for adapting based on the user's browser Data Saver preferences.

@@ -93,5 +102,15 @@ ```js

`saveData` values can be `true` or `false`.
This hook accepts an optional `initialSaveDataStatus` boolean argument, which can be used to provide a `saveData` state value when the user's browser does not support the relevant [NetworkInformation API](https://wicg.github.io/netinfo/). Passing an initial value can also prove useful for server-side rendering, where the developer can pass a server [Save-Data Client Hint](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints#save-data) that has been converted to a boolean to detect the user's data saving preference.
```js
// Inside of a functional React component
const initialSaveDataStatus = true;
const { saveData } = useSaveData(initialSaveDataStatus);
```
### CPU Cores / Hardware Concurrency
`useHardwareConcurrency` React hook for getting the number of logical CPU processor cores of the user's device
`useHardwareConcurrency` utility for adapting to the number of logical CPU processor cores on the user's device.

@@ -113,5 +132,7 @@ ```js

`numberOfLogicalProcessors` values can be the number of logical processors available to run threads on the user's device.
### Memory
`useMemoryStatus` React hook for getting memory status of the device
`useMemoryStatus` utility for adapting based on the user's device memory (RAM)

@@ -133,2 +154,12 @@ ```js

`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](https://github.com/w3c/device-memory). Passing an initial value can also prove useful for server-side rendering, where the developer can pass a server [Device-Memory Client Hint](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints#save-data) to detect the memory capacity of the user's device.
```js
// Inside of a functional React component
const initialMemoryStatus = { deviceMemory: 4 };
const { deviceMemory } = useMemoryStatus(initialMemoryStatus);
```
### Adaptive Code-loading & Code-splitting

@@ -138,3 +169,3 @@

Deliver a light, interactive core experience to users and progressively add high-end-only features on top, if a users hardware can handle it. Below is an example using the Network Status hook:
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:

@@ -149,3 +180,3 @@ ```js

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

@@ -159,3 +190,3 @@ return (

);
}
};

@@ -170,3 +201,3 @@ export default MyComponent;

const Light = ({ imageUrl, ...rest }) => (
<img src={imageUrl} alt='product' {...rest} />
<img src={imageUrl} {...rest} />
);

@@ -182,3 +213,3 @@

const Heavy = ({ imageUrl, ...rest }) => (
const Full = ({ imageUrl, ...rest }) => (
<Magnifier src={imageUrl} {...rest} />

@@ -199,25 +230,28 @@ );

const effectiveType = navigator.connection ? navigator.connection.effectiveType : null
let module;
switch (effectiveType) {
case "3g":
return import(/* webpackChunkName: "light" */ "./light.js");
case '3g':
module = import(/* webpackChunkName: "light" */ './Light.js');
break;
case "4g":
return import(/* webpackChunkName: "full" */ "./full.js");
case '4g':
module = import(/* webpackChunkName: "full" */ './Full.js');
break;
default:
return import(/* webpackChunkName: "full" */ "./full.js")
module = import(/* webpackChunkName: "full" */ './Full.js');
break;
}
return module;
});
function App() {
const App = () => {
return (
<div className="App">
<header className="App-header">
<Suspense fallback={<div>Loading...</div>}>
<Component />
</Suspense>
</header>
<div className='App'>
<Suspense fallback={<div>Loading...</div>}>
<Component />
</Suspense>
</div>
);
}
};

@@ -266,7 +300,11 @@ export default App;

* [Memory considerate loading (SketchFab version)](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cra-memory-considerate-loading-sketchfab) with create-react-app ([Live](https://adaptive-loading.web.app/cra-memory-considerate-loading-sketchfab/))
* [Memory-considerate animation-toggling](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cna-memory-considerate-animation) with create-next-app ([Live](https://cna-memory-animation.firebaseapp.com/))
* [Memory-considerate animation-toggling](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/cna-memory-considerate-animation) with create-next-app ([Live](https://adaptive-loading.web.app/cna-memory-considerate-animation/))
* [React Dixie Mesh - memory considerate loading](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-dixie-memory-considerate-loading) ([Live](https://adaptive-loading.web.app/react-dixie-memory-considerate-loading/))
### Hybrid
* [React Youtube - adaptive loading with mix of network, memory and hardware concurrency](https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-youtube-adaptive-loading) ([Live](https://adaptive-loading.web.app/react-youtube-adaptive-loading/))
## References

@@ -273,0 +311,0 @@

@@ -18,14 +18,18 @@ /*

let unsupported;
if ('connection' in navigator && 'saveData' in navigator.connection) {
unsupported = false;
} else {
unsupported = true;
}
const saveData = unsupported ? null : navigator.connection.saveData === true;
const useSaveData = (initialSaveDataStatus = null) => {
if ('connection' in navigator && 'saveData' in navigator.connection) {
unsupported = false;
} else {
unsupported = true;
}
const useSaveData = () => {
return { unsupported, saveData };
return {
unsupported,
saveData: unsupported
? initialSaveDataStatus
: navigator.connection.saveData === true
};
};
export { useSaveData };

@@ -28,5 +28,16 @@ /*

const { result } = renderHook(() => useSaveData());
expect(result.current.unsupported).toBe(true);
expect(result.current.saveData).toEqual(null);
});
test('should return initialSaveDataStatus for unsupported case', () => {
const initialSaveDataStatus = true;
const { useSaveData } = require('./');
const { result } = renderHook(() => useSaveData(initialSaveDataStatus));
expect(result.current.unsupported).toBe(true);
expect(result.current.saveData).toBe(initialSaveDataStatus);
});
test(`should return "true" for enabled save data`, () => {

@@ -39,2 +50,3 @@ global.navigator.connection = {

expect(result.current.unsupported).toBe(false);
expect(result.current.saveData).toEqual(navigator.connection.saveData);

@@ -50,4 +62,17 @@ });

expect(result.current.unsupported).toBe(false);
expect(result.current.saveData).toEqual(navigator.connection.saveData);
});
test('should not return initialSaveDataStatus for supported case', () => {
const initialSaveDataStatus = false;
global.navigator.connection = {
saveData: true
};
const { useSaveData } = require('./');
const { result } = renderHook(() => useSaveData(initialSaveDataStatus));
expect(result.current.unsupported).toBe(false);
expect(result.current.saveData).toEqual(navigator.connection.saveData);
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc