Skip to content

Commit

Permalink
feat(reactive-react): support Observer Component like vue slot
Browse files Browse the repository at this point in the history
  • Loading branch information
janryWang committed Jun 21, 2021
1 parent 4eb2af0 commit a49ee26
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 11 deletions.
106 changes: 102 additions & 4 deletions packages/react/docs/api/shared/observer.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# observer

## 描述
## observer

### 描述

observer 是一个 [HOC](https://reactjs.bootcss.com/docs/higher-order-components.html),用于为 react 函数组件添加 reactive 特性。

## 什么时候使用
### 什么时候使用

当一个组件内部使用了 [observable](https://reactive.formilyjs.org/api/observable) 对象,而你希望组件响应 observable 对象的变化时。

## API 定义
### API 定义

```ts
interface IObserverOptions {
Expand All @@ -30,6 +32,102 @@ function observer<P, Options extends IObserverOptions>(
>
```

## 注意
### 用例

```tsx
/**
* defaultShowCode: true
*/
import React from 'react'
import { observable } from '@formily/reactive'
import { observer } from '@formily/reactive-react'
const obs = observable({
value: 'Hello world',
})
export default observer(() => {
return (
<div>
<div>
<input
style={{
height: 28,
padding: '0 8px',
border: '2px solid #888',
borderRadius: 3,
}}
value={obs.value}
onChange={(e) => {
obs.value = e.target.value
}}
/>
</div>
<div>{obs.value}</div>
</div>
)
})
```

### 注意

`observer` 只能接收 callable 函数组件,不支持 `React.forwardRef` | `React.memo` 等包裹的组件。

## Observer

### 描述

类似于 Vue 的响应式 Slot,它接收一个 Function RenderProps,只要在 Function 内部消费到的任何响应式数据,都会随数据变化而自动重新渲染,也更容易实现局部精确渲染

其实该 APIFormConsumer 的作用基本一致,只是 FormConsumerRenderProps 参数种透出了当前上下文的 form 实例

### 签名

```ts
interface IObserverProps {
children?: () => React.ReactElement
}
type Observer = React.FC<IObserverProps>
```

### 用例

```tsx
/**
* defaultShowCode: true
*/
import React from 'react'
import { observable } from '@formily/reactive'
import { Observer } from '@formily/react'
const obs = observable({
value: 'Hello world',
})
export default () => {
return (
<div>
<div>
<Observer>
{() => (
<input
style={{
height: 28,
padding: '0 8px',
border: '2px solid #888',
borderRadius: 3,
}}
value={obs.value}
onChange={(e) => {
obs.value = e.target.value
}}
/>
)}
</Observer>
</div>
<Observer>{() => <div>{obs.value}</div>}</Observer>
</div>
)
}
```
4 changes: 2 additions & 2 deletions packages/react/src/shared/connect.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { isFn, isStr, FormPath, each, isValid } from '@formily/shared'
import { isVoidField } from '@formily/core'
import { observer } from '@formily/reactive-react'
import { observer, Observer } from '@formily/reactive-react'
import { JSXComponent, IComponentMapper, IStateMapper } from '../types'
import { useField } from '../hooks'
import hoistNonReactStatics from 'hoist-non-react-statics'
Expand Down Expand Up @@ -86,4 +86,4 @@ export function connect<T extends JSXComponent>(
return Destination
}

export { observer }
export { observer, Observer }
10 changes: 8 additions & 2 deletions packages/reactive-react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { forwardRef, memo } from 'react'
import React, { forwardRef, memo, Fragment } from 'react'
import hoistNonReactStatics from 'hoist-non-react-statics'
import { useObserver } from './hooks'
import { IObserverOptions } from './types'
import { IObserverOptions, IObserverProps } from './types'

export function observer<P, Options extends IObserverOptions>(
component: React.FunctionComponent<P>,
Expand Down Expand Up @@ -38,3 +38,9 @@ export function observer<P, Options extends IObserverOptions>(

return memoComponent
}

export const Observer = observer((props: IObserverProps) => {
const children =
typeof props.children === 'function' ? props.children() : props.children
return React.createElement(Fragment, {}, children)
})
6 changes: 6 additions & 0 deletions packages/reactive-react/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import React from 'react'

export interface IObserverOptions {
forwardRef?: boolean
scheduler?: (updater: () => void) => void
displayName?: string
}

export interface IObserverProps {
children?: (() => React.ReactElement) | React.ReactNode
}
65 changes: 62 additions & 3 deletions packages/reactive/docs/api/react/observer.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# observer

## 描述
## observer

### 描述

在 React 中,将 Function Component 变成 Reaction,每次视图重新渲染就会收集依赖,依赖更新会自动重渲染

<Alert>
注意:只支持Function Component
</Alert>

## 签名
### 签名

```ts
interface IObserverOptions {
Expand All @@ -22,7 +24,7 @@ interface observer<T extends React.FC> {
}
```

## 用例
### 用例

```tsx
/**
Expand Down Expand Up @@ -58,3 +60,60 @@ export default observer(() => {
)
})
```

## Observer

### 描述

类似于 Vue 的响应式 Slot,它接收一个 Function RenderProps,只要在 Function 内部消费到的任何响应式数据,都会随数据变化而自动重新渲染,也更容易实现局部精确渲染

### 签名

```ts
interface IObserverProps {
children?: () => React.ReactElement
}

type Observer = React.FC<IObserverProps>
```
### 用例
```tsx
/**
* defaultShowCode: true
*/
import React from 'react'
import { observable } from '@formily/reactive'
import { Observer } from '@formily/reactive-react'

const obs = observable({
value: 'Hello world',
})

export default () => {
return (
<div>
<div>
<Observer>
{() => (
<input
style={{
height: 28,
padding: '0 8px',
border: '2px solid #888',
borderRadius: 3,
}}
value={obs.value}
onChange={(e) => {
obs.value = e.target.value
}}
/>
)}
</Observer>
</div>
<Observer>{() => <div>{obs.value}</div>}</Observer>
</div>
)
}
```

0 comments on commit a49ee26

Please sign in to comment.