Skip to content

Commit

Permalink
feat: 初始化
Browse files Browse the repository at this point in the history
  • Loading branch information
三少 committed Jul 27, 2021
1 parent 7aa73e9 commit 4f271a1
Show file tree
Hide file tree
Showing 7 changed files with 436 additions and 0 deletions.
17 changes: 17 additions & 0 deletions packages/global-state/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
*.log
*.lock
*.swp

.github
.cache
.temp
.idea
.rn_temp
.DS_Store

CHANGELOG.md
package-lock.json

src
node_modules
coverage
28 changes: 28 additions & 0 deletions packages/global-state/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# @antmjs/global-state

> 全局UI数据缓存解决方案
## 为什么需要

我所理解的缓存分4类,1: 和UI无关的临时缓存;2: 和UI无关的本地缓存; 3: 和UI相关的全局缓存; 4: 和UI相关的局部缓存。本仓库解决的就是3的问题。
众所周知,这一类的解决方案其实有很多,比如redux、mobx,重点其实在于我们的项目适不适合这些比较重的解决方案。结合面试者的回答,有都走redux的,有请求走redux的,有自己约定的。其实都是在找一个临界点,而这个临界点结合业务以及一线小伙伴的理解都会产生不一样的结果,最终redux就成了一个黑洞。
我现在更倾向于少用,当然不是不用,而是可枚举的用(你很清楚里面存储了什么东西,和你使用@antmjs/cache这个库一样,你很清楚你定义了哪些key),而且由负责人维护,结合hooks,业务开发者只管取值。可以看看[模版](https://github.com/AntmJS/temptaro)

## 安装

```bash
yarn add @antmjs/global-state
```

## 使用

```js
import GlobalState from '@antmjs/state'

const {
Provider,
useGlobalState,
useGlobalLoading,
useGlobalError,
} = GlobalState({ user: {} }, { user: async function () { /** await getUser */ } })
```
59 changes: 59 additions & 0 deletions packages/global-state/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"name": "@antmjs/global-state",
"version": "0.0.0",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"typings": "types/index.d.ts",
"author": "三少 <hi_sanshao@outlook.com>",
"description": "全局UI缓存解决方案",
"license": "MIT",
"publishConfig": {
"access": "public",
"registry": "http://registry.npmjs.org"
},
"keywords": [
"cache",
"globalState",
"taro",
"react"
],
"repository": {
"type": "https",
"url": "https://github.com/AntmJS/antm.git"
},
"bugs": {
"url": "https://github.com/AntmJS/antm/issues/new"
},
"engines": {
"node": ">=12",
"npm": ">=6.4",
"yarn": ">=1.22"
},
"browserslist": [
"Chrome >= 35",
"ChromeAndroid >= 35",
"iOS >= 8",
"Safari >= 8",
"Android >= 4.1",
"QQAndroid >= 4.1",
"UCAndroid >= 4.1"
],
"scripts": {
"_clean": "npx rimraf dist",
"_real": "npx rollup -c ./rollup.config.js",
"watch": "npx rollup -c ./rollup.config.js -w",
"build": "run-s _clean _real",
"test:watch": "",
"test": ""
},
"peerDependencies": {
"@types/react": ">=17.0.0",
"react": ">=17.0.0",
"react-dom": ">=17.0.0"
},
"dependencies": {
"@babel/runtime-corejs3": "^7.14.7",
"scheduler": "^0.20.2",
"use-context-selector": "^1.3.7"
}
}
47 changes: 47 additions & 0 deletions packages/global-state/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const { resolve, join } = require('path')
const { getBabelOutputPlugin } = require('@rollup/plugin-babel')
const json = require('@rollup/plugin-json')
const typescript = require('@rollup/plugin-typescript')
const commonjs = require('@rollup/plugin-commonjs')
const { nodeResolve } = require('@rollup/plugin-node-resolve')
const cwd = process.cwd()

const config = {
input: join(cwd, 'src/index.ts'),
output: [
{
file: join(cwd, 'dist/index.js'),
format: 'cjs',
exports: 'default',
sourcemap: true,
},
{
sourcemap: true,
format: 'esm',
exports: 'default',
file: join(cwd, 'dist/index.esm.js'),
},
],
external: ['@babel/runtime-corejs3', 'react'],
plugins: [
commonjs({
include: /\/node_modules\//,
}),
nodeResolve({
customResolveOptions: {
moduleDirectories: ['node_modules'],
},
}),
json(),
typescript({
allowSyntheticDefaultImports: true,
jsx: true,
}),
getBabelOutputPlugin({
configFile: resolve(__dirname, '../../babel.config.js'),
}),
],
}

module.exports = [config]
132 changes: 132 additions & 0 deletions packages/global-state/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { useRef, useState } from 'react'

import { createContext, useContextSelector } from 'use-context-selector'
import GlobalState from '../types/index.d'

export default function <
TData extends GlobalState.IAnyObject,
TFetch extends GlobalState.IPromiseFunctionObject<TData>,
>(data: TData, fetch: TFetch): GlobalState.IMethod<TData, TFetch> {
// 全局数据使用的context
const context = createContext(null) as React.Context<
| null
| [
GlobalState.StateOpt<{ [K in keyof TData]: TData[K] }>,
React.Dispatch<React.SetStateAction<TData>>,
]
>

// 远程请求数据状态变更使用的context
const loadingContext = createContext(null) as React.Context<
null | [Partial<{ [K in keyof TFetch]: boolean }>, any]
>

// 远程请求数据发生错误使用的context
const errorContext = createContext(null) as React.Context<
| null
| [Partial<{ [K in keyof TFetch]: { code: string; message: string } }>, any]
>

// 更新全局数据使用的context
const fetchContext = createContext({
fetchAndUpdate: (key: keyof TFetch, params?: any) => {
console.error('调用失败,还未初始化', key, params)
},
update: <T extends keyof TData>(
key: T,
value: Partial<GlobalState.StateOpt<{ [K in keyof TData]: TData[K] }>>[T],
) => {
console.error('调用失败,还未初始化', key, value)
},
})

const Provider = ({ children }: any) => {
const [state, setState] = useState(data)
const [loading, setLoading] = useState({})
const [error, setError] = useState({})
const ins = useRef({
fetchAndUpdate: async (key: keyof TFetch, params?: any) => {
if (fetch[key]) {
const { data, error } = await fetch[key]!(params)
if (!error) {
setState((pre) => {
return { ...pre, [key]: data }
})
}
}
},
update: <T extends keyof TData>(
key: T,
value: Partial<
GlobalState.StateOpt<{ [K in keyof TData]: TData[K] }>
>[T],
) => {
console.error('调用失败,还未初始化', key, value)
},
})

return (
<context.Provider value={[state, setState]}>
<fetchContext.Provider value={ins.current}>
<loadingContext.Provider value={[loading, setLoading]}>
<errorContext.Provider value={[error, setError]}>
{children}
</errorContext.Provider>
</loadingContext.Provider>
</fetchContext.Provider>
</context.Provider>
)
}

function useGlobalState<T extends keyof TData>(
key: T,
): Partial<GlobalState.StateOpt<{ [K in keyof TData]: TData[K] }>>[T] {
return useContextSelector(context, (v) => v?.[0][key])
}

function useGlobalLoading<T extends keyof TData>(
key?: T,
): Partial<{ [K in keyof TFetch]: boolean }> {
console.log(key)
return {}
}

function useGlobalError<T extends keyof TData>(
key?: T,
): Partial<{ [K in keyof TFetch]: { code: string; message: string } }> {
console.log(key)
return {}
}

function useUpdate(): GlobalState.IUpdate<TData, TFetch> {
return {
fetchAndUpdate: async (key: keyof TFetch, params?: any) => {
if (fetch[key]) {
const { data, error } = await fetch[key]!(params)
console.log(data, error)
// if (!error) {
// setState((pre) => {
// return { ...pre, [key]: data }
// })
// }
}
},
update: <T extends keyof TData>(
key: T,
value: Partial<
GlobalState.StateOpt<{ [K in keyof TData]: TData[K] }>
>[T],
) => {
console.error('调用失败,还未初始化', key, value)
},
}
}

return {
Provider,
useGlobalState,
useGlobalLoading,
useGlobalError,
useUpdate,
}
}
61 changes: 61 additions & 0 deletions packages/global-state/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
declare namespace GlobalState {
type Record<K extends keyof any, T> = {
[P in K]: T
}
type IAnyObject = Record<string, any>
type NoneEmptyArray<T> = [T, ...T[]]
type IFunctionObject = Record<string, (arg?: any) => any>
type IPromiseFunctionObject<TData> = Partial<
{
[K in keyof TData]: (
params?: any,
) => Promise<{ data: any; error?: { code: string; message: string } }>
}
>

type StateOpt<T> = {
[K in keyof T]: T[K] extends null | undefined
? any
: T[K] extends IAnyObject
? T[K] & IAnyObject
: T[K]
}

interface IUpdate<
TData extends IAnyObject,
TFetch extends IPromiseFunctionObject<TData>,
> {
fetchAndUpdate: (key: keyof TFetch, params?: any) => Promise<void>
update: <T extends keyof TData>(
key: T,
value: Partial<StateOpt<{ [K in keyof TData]: TData[K] }>>[T],
) => void
}

interface IMethod<
TData extends IAnyObject,
TFetch extends IPromiseFunctionObject<TData>,
> {
Provider: ({ children }: any) => JSX.Element
useGlobalState: <T extends keyof TData>(
key: T,
) => Partial<StateOpt<{ [K in keyof TData]: TData[K] }>>[T]

useGlobalLoading: <T extends keyof TData>(
key?: T,
) => Partial<{ [K in keyof TFetch]: boolean }>

useGlobalError: <T extends keyof TData>(
key?: T,
) => Partial<{ [K in keyof TFetch]: { code: string; message: string } }>

useUpdate: () => IUpdate<TData, TFetch>
}
}

declare function GlobalState<
TData extends IAnyObject,
TFetch extends IPromiseFunctionObject<TData>,
>(data: TData, fetch: TFetch): GlobalState.IMethod<TData, TFetch>

export default GlobalState
Loading

0 comments on commit 4f271a1

Please # to comment.