Skip to content

angelrong520/reto

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Reto

             ___                  __ 
            / _ \___    ___ _____/ /_
           / , _/ -_)  / _ `/ __/ __/
   ____   /_/|_|\__/   \_,_/\__/\__/ 
  / __/  / /____     _______         
 _\ \   / __/ _ \   / __/ -_)        
/___/   \__/\___/  /_/  \__/         
                                     

简单而高效的React Store

特性

  • 使用各种react hooks定义store,你熟悉的useState、useEffect都在这里
  • 可定义多个store,随用随取
  • 基于React Context的依赖注入,简单但不失灵活
  • 强类型支持,但同时兼容js环境

安装

$ yarn add reto
# or
$ npm install reto --save

一个简单的例子

每一个Store其实就是一个类似于custom hook的函数。在Store的函数体中,你可以随意使用react hooks,例如useStateuseEffectuseRef

export function FooStore() {
  const [x, setX] = useState(initial)
  return {
    x,
    setX
  }
}

通过Provider组件提供一个FooStore

<Provider of={FooStore}>
  <App/>
</Provider>

在组件中通过useStore获取并订阅FooStore的更新。

const App: FC = (props) => {
  const fooStore = useStore(FooStore)
  
  function changeStore() {
    fooStore.setX(fooStore.x + 1)
  }
  return (
    <div>
      <button onClick={changeStore}>Change</button>
      {fooStore.x}
    </div>
  )
}

当点击按钮时,会调用fooStore中的setX函数,从而触发x的更新以及App组件的重渲染,一切都非常简单而自然。

进阶用法

传递参数给Store

可以通过Provider的args属性传递参数给Store:

export function FooStore(initial = 1) {
  const [x, setX] = useState(initial)
  return {
    x,
    setX
  }
}
<Provider of={FooStore} args={[5]}>
  <App/>
</Provider>

多层级的Store树

Reto在底层使用的是React的context API,因此可以创建出多层级的Store树。

- Provider of FooStore -> 记为实例A
  |- Cosumer of FooStore -> 拿到的是实例A
  |- Provider of FooStore -> 记为实例B
     |- Consumer of FooStore -> 拿到的是实例B

Store之间的依赖

你可以在一个Store的函数体中使用useStore来引入另一个Store:

export function BarStore() {
  const fooStore = useStore(FooStore)
  const [x, setX] = useState(2)
  return {
    x,
    setX,
    fooStore,
  }
}

同时,Reto会将其作为本Store的依赖。因此,当FooStore更新时,BarStore也会自动跟着更新。

使用immer

使用immer可以在某些时候简化setState的语法,详细用法可以参考其官方文档上的介绍

使用useReducer

当Store变得非常复杂时,可以通过useReducer更好的组织逻辑。详见文档

使用useAsyncMemo

useAsyncMemo非常适合用来处理异步的计算数据,例如在翻页(pageNumber变化)时调用API加载远程数据。

使用useAction

由于useEffect是会延后执行的,因此在Reto中,useAction是一个更适合的替代品。

Unlike componentDidMount and componentDidUpdate, the function passed to useEffect fires after layout and paint, during a deferred event. This makes it suitable for the many common side effects, like setting up subscriptions and event handlers, because most types of work shouldn’t block the browser from updating the screen.

使用useCreation

useCreationuseMemo的替代品,因为useMemo不能保证被memo的值一定不会被重计算。

**You may rely on useMemo as a performance optimization, not as a semantic guarantee.**In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

使用Rx.js

Rx.js非常适合事件通知的逻辑,在Reto中,可以配合useCreation使用Rx.js:

export function FooStore() {
  const event$ = useCreation(() => new Subject()) // 使用useCreation来避免重复创建Subject

  function notify(data) {
    event$.next(data)
  }

  return {
    opened$,
    notify,
  }
}

About

React store with hooks.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 60.5%
  • JavaScript 39.5%