3.0.1 • Published 3 years ago

hostate v3.0.1

Weekly downloads
13
License
MIT
Repository
github
Last release
3 years ago

Hostate

基于React Hooks实现局部的状态管理,类比Custom Hook,Hostate强调基于功能设计Store。

Installation

npm:npm i hostate

yarn:yarn add hostate

API

createScopeStore(initialState, actionCreators)

ScopeStore Examples: Counter
import React from "react"
import {createScopeStore} from "hostate"

// 创建counterStore
const initialState: number = 0
const actionCreators = {
  reset() {
    return initialState
  },
  inc() {
    return (c: number) => c + 1
  },
  dec() {
    return (c: number) => c - 1
  }
}

const {
  Provider: CounterProvider,
  useStore,
  useActions
} = createScopeStore(initialState, actionCreators)

// 直接在组件中使用,上层组件需要用Provider包裹
function ChildInStore({ id = "0", btns = ["+", "-"] }) {
  // 返回[state, actions]
  const [count, { inc, dec }] = useStore()
  return (
    <div>
      Child{id} in Store----count:{count}
      {btns.includes("+") && <button onClick={inc}>+</button>}
      {btns.includes("-") && <button onClick={dec}>-</button>}
    </div>
  )
}

const ResetBtn = () => {
  // 读写分离,重置组件时只触发action,不会重渲染
  const { reset } = useActions()
  return <button onClick={reset}>reset</button>
}

export default function Counter() {
  return (
    <CounterProvider>
      <ChildInStore btns={["+"]} />
      <ChildInStore id="1" btns={["-"]} />
      <ResetBtn />
    </CounterProvider>
  )
}
ScopeStore Examples: Info
import React from "react"
import {createScopeStore} from "hostate"

// 创建infoStore
interface StateType {
  name: string
  age: number
  gender: number
}

const initialState = {
  name: "Tony",
  age: 23,
  gender: 0
}
const actionCreators = {
  setInfo(prevInfo: StateType, newInfo: Partial<StateType>) {
    return { ...prevInfo, ...newInfo }
  },
  resetInfo() {
    return initialState
  }
}

// 订阅state
const subscriptions = {
  // 只订阅了name,只有name改变了才会重渲染
  name: (info: StateType) => info.name,
  // 订阅了gender,实现衍生数据
  gender: (info: StateType) => (info.gender === 0 ? "女" : "男")
}

const infoStore = createScopeStore(initialState, actionCreators, subscriptions)

// 直接在组件中使用,上层组件需要用Provider包裹
function Info() {
  console.log(`Info render`)
  const [info, { setInfo, resetInfo }] = infoStore.useStore()
  return (
    <div>
      <p>info: {JSON.stringify(info)}</p>
      <button onClick={() => setInfo({ name: "Bob" })}>setName</button>
      <button onClick={() => setInfo({ age: 30 })}>setAge</button>
      <button onClick={() => setInfo({ gender: 1 })}>setGender</button>
      <button onClick={() => resetInfo()}>reset</button>
    </div>
  )
}

function InfoName() {
  console.log(`InfoName render`)
  const name = infoStore.useSubscribe("name")
  return (
    <div>
      <p>name: {name}</p>
    </div>
  )
}

function InfoGender() {
  console.log(`InfoGender render`)
  const gender = infoStore.useSubscribe("gender")
  return (
    <div>
      <p>gender: {gender}</p>
    </div>
  )
}

export default function Info() {
  return (
    <infoStore.Provider>
      <Info />
      <InfoName />
      <InfoGender />
    </infoStore.Provider>
  )
}

withStores(Component, stores) | <Provider stores={stores}>...</Provider>

composeStores Examples: InfoCounter
import React from "react"
import {createScopeStore, Provider} from "hostate"

// 创建counterStore
const initialState: number = 0
const actionCreators = {
  reset() {
    return initialState
  },
  inc() {
    return (c: number) => c + 1
  },
  dec() {
    return (c: number) => c - 1
  }
}
const counterStore = createScopeStore(initialState, actionCreators)

// 创建infoStore
export interface StateType {
  name: string
  age: number
}
const initialState = {
  name: "Tony",
  age: 23
}
const actionCreators = {
  // 默认把prevState当作第一个参数传入,在调用store返回的actions中无需传递prevState
  setInfo(prevInfo: StateType, newInfo: Partial<StateType>) {
    return { ...prevInfo, ...newInfo }
  },
  resetInfo() {
    return initialState
  }
}
const infoStore = createScopeStore(initialState, actionCreators)

// 组合stores
const stores = [counterStore, infoStore]

// 直接在组件中使用,上层组件需要用Provider包裹
function Counter() {
  console.log(`Counter render`)
  const [count, { inc, dec, reset}] = counterStore.useStore()
  return (
    <div>
      count:{count}
      <button onClick={inc}>+</button>
      <button onClick={dec}>-</button>
      <button onClick={reset}>reset</button>
    </div>
  )
}

function Info() {
  console.log(`Info render`)
  const [info, { setInfo, resetInfo }] = infoStore.useStore()
  return (
    <div>
      <p>info: {JSON.stringify(info)}</p>
      <button onClick={() => setInfo({ name: "Bob" })}>setName</button>
      <button onClick={() => setInfo({ age: 30 })}>setAge</button>
      <button onClick={() => resetInfo()}>reset</button>
    </div>
  )
}

// use Provider to compose stores
export default function App() {
  return (
    <Provider stores={stores}>
      <Counter />
      <Info />
    </Provider>
  );
}

// use withStores to compose stores

// function App() {
//   return (
//     <>
//       <Counter />
//       <Info />
//     </>
//   );
// }

// export default withStores(App, stores);
3.0.1

3 years ago

3.0.0

3 years ago

2.3.1

3 years ago

2.3.0

3 years ago

2.2.3

3 years ago

2.2.2

3 years ago

2.2.1

3 years ago

2.2.0

3 years ago

2.1.0

3 years ago

2.0.0

3 years ago

1.1.0

3 years ago

1.0.0

3 years ago