react-offscreen(activity)
react-offscreen 可以隐藏组件而无需卸载它们
特性
- 基于 Suspense
- minzip 仅 1.2kb
- 良好的性能
- 完全支持 React 上下文
安装
npm install @ivliu/react-offscreen
yarn add @ivliu/react-offscreen
pnpm add @ivliu/react-offscreen
示例
基本用法
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
import { Activity } from '@ivliu/react-offscreen';
const Counter = () => {
const [count, setCount] = useState(0);
return <p onClick={() => setCount(count + 1)}>{count}</p>;
};
const App = () => {
const [open, setOpen] = useState(false);
return (
<div>
<button onClick={() => setVisible(!open)}>{open}</button>
<Activity mode={open ? 'visible' : 'hidden'}>
<Counter />
</Activity>
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
与 createPortal 一起使用
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
import { createPortal } from 'react-dom';
import { Activity } from '@ivliu/react-offscreen';
const Counter = () => {
const [count, setCount] = useState(0);
return createPortal(
<button type="button" onClick={() => setCount(count + 1)}>计数为 {count}</button>,
document.body,
);
};
const App = () => {
const [open, setOpen] = useState(false);
return (
<div>
<button onClick={() => setVisible(!open)}>{open}</button>
<Activity mode={open ? 'visible' : 'hidden'}>
<Counter />
</Activity>
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
与 React.lazy 一起使用
由于 Activity 是基于 Suspense 实现的,使用时请注意将 Suspense 组件放在 Activity 组件下方,否则可能会导致 fallback 无法正常显示的问题。
import { useState, lazy, Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import { Activity } from '@ivliu/react-offscreen';
const LazyCount = lazy(() => import('./Count'));
const Count = () => {
const [count, setCount] = useState(0);
return <p onClick={() => setCount(count + 1)}>{count}</p>;
};
const App = () => {
const [open, setOpen] = useState(false);
return (
<div>
<button onClick={() => setVisible(!open)}>{open}</button>
<Activity mode={open ? 'visible' : 'hidden'}>
<Suspense fallback="加载中...">
<LazyCount />
</Suspense>
</Activity>
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
重命名为 Activity
为了与官方 React 保持一致,我们将 Offscreen 重命名为 Activity。同时,我们仍会导出 Offscreen
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
import { Activity, Offscreen } from '@ivliu/react-offscreen';
const Count = () => {
const [count, setCount] = useState(0);
return <p onClick={() => setCount(count + 1)}>{count}</p>;
};
const App = () => {
const [open, setOpen] = useState(false);
return (
<div>
<button onClick={() => setVisible(!open)}>{open}</button>
<Activity mode={open ? 'visible' : 'hidden'}>
<Count />
</Activity>
<Offscreen mode={open ? 'visible' : 'hidden'}>
<Count />
</Offscreen>
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
TypeScript
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
import { Activity } from '@ivliu/react-offscreen';
import type { ActivityMode } from '@ivliu/react-offscreen';
const Count = () => {
const [count, setCount] = useState(0);
return <p onClick={() => setCount(count + 1)}>{count}</p>;
};
const App = () => {
const [mode, setMode] = useState<ActivityMode>('visible');
return (
<div>
<button onClick={() => setMode(mode === 'visible' ? 'hidden' : 'visible')}>{mode}</button>
<Activity mode={mode}>
<Count />
</Activity>
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
不稳定的钩子
我们为组件激活和停用状态提供了钩子实现,但我们不打算将其合并到主分支中。如果你需要它,请参考 https://github.com/IVLIU/react-offscreen/tree/feat/unstable-hooks
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Offscreen, useActivated } from 'react-offscreen';
const Count = () => {
const [count, setCount] = React.useState(0);
useActivated(() => {
console.log('已激活');
return () => {
console.log('已停用')
}
});
return <p onClick={() => setCount(count + 1)}>{count}</p>;
};
const App = () => {
const [visible, setVisible] = React.useState(false);
return (
<div>
<button onClick={() => setVisible(!visible)}>{visible}</button>
<Offscreen mode={visible ? 'visible' : 'hidden'}>
<Count />
</Offscreen>
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
注意
请使用 React 16.8 及以上版本