Skip to content

Commit

Permalink
feat: rewrite readme
Browse files Browse the repository at this point in the history
  • Loading branch information
IVLIU committed Mar 8, 2024
1 parent ebf011b commit e6b810a
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 45 deletions.
112 changes: 67 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,71 @@
# react-offscreen
# react-offscreen(activity)

keep-alive的react版本,它基于Suspense实现,通过它我们可以实现在切换组件时不销毁组件,以达到状态保持的目的。
![NPM Version](https://img.shields.io/npm/v/%40ivliu%2Freact-offscreen)
![License](https://img.shields.io/badge/license-MIT-yellow)

## 使用方法
react-offscreen can hide components without uninstalling them

## Features

- based on Suspense
- minzip only 1.2kb
- good performance
- react full context support

## Installation

```bash
npm install @ivliu/react-offscreen
yarn add @ivliu/react-offscreen
pnpm add @ivliu/react-offscreen
```

## Examples

### Basic usage

```typescript
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
import { Offscreen } from '@ivliu/react-offscreen';
import { Activity } from '@ivliu/react-offscreen';

const Count = () => {
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>
<Offscreen mode={open ? 'visible' : 'hidden'}>
<Count />
</Offscreen>
<Activity mode={open ? 'visible' : 'hidden'}>
<Counter />
</Activity>
</div>
);
};

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```

## 注意

### 配合lazy组件使用

由于Activity(Offscreen)组件是基于Suspense实现的,所以当配合lazy且需要设置fallback需要注意嵌套顺序。
### Use with createPortal

```typescript
import { useState, lazy, Suspense } from 'react';
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
import { createPortal } from 'react-dom';
import { Activity } from '@ivliu/react-offscreen';

const LazyCount = lazy(() => import('./Count'));

const Count = () => {
const Counter = () => {
const [count, setCount] = useState(0);

return <p onClick={() => setCount(count + 1)}>{count}</p>;
return createPortal(
<button type="button" onClick={() => setCount(count + 1)}>count is {count}</button>,
document.body,
);
};

const App = () => {
Expand All @@ -55,10 +74,7 @@ const App = () => {
<div>
<button onClick={() => setVisible(!open)}>{open}</button>
<Activity mode={open ? 'visible' : 'hidden'}>
{/** Suspense应该在Activity(Offscreen)组件下 */}
<Suspense fallback="loading...">
<LazyCount />
</Suspense>
<Counter />
</Activity>
</div>
);
Expand All @@ -67,16 +83,16 @@ const App = () => {
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```

### 结合concurrent feature使用

如果你使用的react18,且使用了concurrent feature,那么需要注意由concurrent feature触发更新引起的组件挂起不会切换回退显示,所以如果出现mode为hidden时子组件无法隐藏的话请改为同步渲染即可正常工作,具体可以参考https://zh-hans.react.dev/reference/react/Suspense#preventing-already-revealed-content-from-hiding
### Use with React.lazy

#### 错误示例
> Since Activity is implemented based on Suspense, please pay attention to placing the Suspense component under the Activity component when using it, otherwise it may cause the problem that the fallback cannot be displayed normally.
```typescript
import { useState, startTransition } from 'react';
import { useState, lazy, Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import { Offscreen } from '@ivliu/react-offscreen';
import { Activity } from '@ivliu/react-offscreen';

const LazyCount = lazy(() => import('./Count'));

const Count = () => {
const [count, setCount] = useState(0);
Expand All @@ -88,24 +104,27 @@ const App = () => {
const [open, setOpen] = useState(false);
return (
<div>
<button onClick={() => startTransition(() => setVisible(!open))}>{open}</button>
<Offscreen mode={open ? 'visible' : 'hidden'}>
<Count />
</Offscreen>
<button onClick={() => setVisible(!open)}>{open}</button>
<Activity mode={open ? 'visible' : 'hidden'}>
<Suspense fallback="loading...">
<LazyCount />
</Suspense>
</Activity>
</div>
);
};

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```
#### 正确示例

移除startTransition或者useDeferredValue即可
## Rename to Activity

> In order to keep pace with the official react, we renamed Offscreen to Activity. At the same time, we will still export Offscreen
```typescript
import { useState } from 'react';
import ReactDOM from 'react-dom/client';
import { Offscreen } from '@ivliu/react-offscreen';
import { Activity, Offscreen } from '@ivliu/react-offscreen';

const Count = () => {
const [count, setCount] = useState(0);
Expand All @@ -115,9 +134,13 @@ const Count = () => {

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>
Expand All @@ -128,14 +151,13 @@ const App = () => {
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```

## Activity导出

由于react官方已经将Offscreen重命名为Activity,为了与官方保持一致,我们同时导出了Offscreen和Activity。
## typescript

```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);
Expand All @@ -144,11 +166,12 @@ const Count = () => {
};

const App = () => {
const [open, setOpen] = useState(false);
const [mode, setMode] = useState<ActivityMode>('visible');

return (
<div>
<button onClick={() => setVisible(!open)}>{open}</button>
<Activity mode={open ? 'visible' : 'hidden'}>
<button onClick={() => setMode(mode === 'visible' ? 'hidden' : 'visible')}>{mode}</button>
<Activity mode={mode}>
<Count />
</Activity>
</div>
Expand All @@ -160,8 +183,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(<App />);

## unstable hooks

我们实验性的支持了激活 失活的hooks,但是它的执行时机是晚于Effect的,这与react未来规划不符,所以我们不准备将其合并至主分支,有兴趣的可以自行fork使用。
具体可以参考https://github.com/IVLIU/react-offscreen/tree/feat/unstable-hooks
> We provide hook implementation for component activation and deactivation status, but we do not plan to merge it into the main branch. If you need it, please refer to https://github.com/IVLIU/react-offscreen/tree/feat/unstable-hooks
```typescript
import React from 'react';
Expand Down Expand Up @@ -196,6 +218,6 @@ const App = () => {
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```

## 注意
## Notice

仅支持react 16.8版本及以上
please use react16.8 and above versions
3 changes: 3 additions & 0 deletions src/canUseDOM.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* @description determine whether it is a browser environment
*/
export const canUseDOM = () =>
!!(
typeof window !== 'undefined' &&
Expand Down

0 comments on commit e6b810a

Please sign in to comment.