Skip to content

Chinese version of hooks view model api

trigkit4 edited this page Nov 23, 2022 · 12 revisions

hooks-view-model

API

hooks-view-model是api友好的ui与业务逻辑解耦方案:

WechatIMG592

通用state状态存储

  • 所有update开头相关的API均为view和viewModel通用的api;
  • 所有use开头的api均为view独享api;
  • updatexxxState本质是useState的updater

updateGlobalStateByKey

通过key更新全局state

参数:

  • key:为了保持key的唯一性,请使用枚举值
  • value:要更新的状态值

例子Examples

type GLOBAL_KEYS =  {
  APP = 'APP'
}
type HeaderVMProps = {
  count: number
}
import StoreViewModel from 'hooks-view-model';
import { GLOBAL_KEYS, HeaderVMProps } from './types'

class HeaderViewModel extends StoreViewModel<HeaderVMProps> {
  updateCount = (count) => {
    this.updateGlobalStateByKey(GLOBAL_KEYS.APP, {
      count,
    });
  };
}

updateCurrentState

相当于setState:

  • 更新当前view的state,view 和 viewModel 适用
  • 自动绑定当前viewModel的name作为key,无需传入key

参数:

  • value:要更新的状态值

例子Examples

import StoreViewModel from 'hooks-view-model';

class HeaderViewModel extends StoreViewModel<any> {
  updatePersonInfo = (person: {
    name: string;
    age?: number;
    height?: number;
  }) => {
    this.updateCurrentState({
      person,
    });
  };
  changeHeaderData = (count) => {
    this.updateCurrentState({
      headCount: count,
    });
  };
}
export { HeaderViewModel };

updateImmerState

使用immer更新state,可实现细粒度更新:

updateImmerState((draft) => {})

View

import React, { useState, useContext } from 'react';
import { useVM } from 'hooks-view-model';
import { FooterViewModel } from './Footer.ViewModel';
import { GLOBAL_KEYS } from '../GlobalStore';

export default function Footer() {
  const {
    useCurrentState,
    updateTodoValue,
    pushNewPerson
  } = useVM(FooterViewModel, {});
  const {
    todo,
    persons
  } = useCurrentState({
    todo: {
      title: 'test',
      done: true,
    },
     persons: [
      { name: 'mike', age: 10 },
      { name: 'john', age: 20 },
    ],
  });
  console.log('updateImmerState1', todo, persons);
  return (
    <div style={{ border: '1px solid red' }}>
      <button onClick={updateTodoValue}>click updateTodoValue</button>
      <button onClick={pushNewPerson}>click pushNewPerson</button>
    </div>
  );
}

ViewModel

//Footer.ViewModel.ts
import { GLOBAL_KEYS } from '../GlobalStore';
import StoreViewModel from 'hooks-view-model';

class FooterViewModel extends StoreViewModel<any> {
  updateTodoValue = () => {
    this.updateImmerState(draft => {
      draft.todo.done = !draft.todo.done;
    });
  };
  pushNewPerson = () => {
    this.updateImmerState(draft => {
      draft.persons.push({ name: 'alvin', age: 30 });
    });
  };
}
export { FooterViewModel };

updateGlobalImmerState(globalKey, (draft) => void)

用法上同,区别是增加了GLOBAL_KEY

  updateGlobalTodoValue = () => {
    this.updateGlobalImmerState(GLOBAL_KEYS.GLOBAL_TODO, draft => {
      draft.global_todo.done = !draft.global_todo.done;
    });
  };

getGlobalStateByKey(key)

通过key获取全局state,view和viewModel 适用

getCurrentState()

获取当前state,view和viewModel 适用

import StoreViewModel from 'hooks-view-model';

class HeaderViewModel extends StoreViewModel<any> {
  changeModule = () => {
     const { tableData } = this.getCurrentState();
  }
}
export { HeaderViewModel };

removeGlobalStateByKey

  • 通过key 移除global state,view和viewModel 适用;
  • GlobalState 在组件卸载时不会自动回收,与reducer 或 redux保持一致
  • currentState 在组件卸载时会自动回收

getGlobalStateByKeys([])

通过keys数组批量获取全局状态值

Store 内存存储

  • 本质是通过new map()实例化的对象,存储在内存中
  • 组件卸载仍会存在,刷新页面或关闭页面,该变量释放

updateGlobalStore

通过key 更新全局变量存储

例子Examples

class FooterViewModel extends StoreViewModel<any> {
  updateCount = count => {
    this.updateCurrentState({
      count,
    });
  };
  updateModalState = () => {
    this.updateGlobalStore('modal_close', true);
  };
  updateLocalValue = () => {
    this.updateGlobalPersistStore('local_value', { name: 'huang', age: 123 });
  };
  removeLocalValue = () => {
    const removed = this.removeGlobalPersistStoreByKey('local_value');
    console.log('removed', removed);
  };
  mounted = () => {
    console.log('mounted123');
  };
}
export { FooterViewModel };

getGlobalStoreByKey

  • 通过key获取全局变量存储
  • 存在时,返回正确的值;不存在时,返回undefined

removeGlobalStoreByKey

  • 通过key 移除全局变量存储,返回布尔值

持久化存储localStorage

PersistStore 本质是存储在localstorage,localStorage有效期为永久,除非手动删除

updateGlobalPersistStore

更新全局持久化存储

参数:

  • key:要更新的键
  • value:要更新的值

例子Examples

class FooterViewModel extends StoreViewModel<any> {

  updateLocalValue = () => {
    this.updateGlobalPersistStore('local_value', { name: 'huang', age: 123 });
  };

}
export { FooterViewModel };

getGlobalPersistStoreByKey

通过key获取全局持久化存储的数据

例子Examples

import { useVM } from 'hooks-view-model';

export default function Footer() {
  const {
    useCurrentState,
    getGlobalPersistStoreByKey,
  } = useVM(FooterViewModel, {});;
  const { count } = useCurrentState({
    count: 0,
  });

  return (
    <div style={{ border: '1px solid red' }}>
      <p>count: {getGlobalPersistStoreByKey('local_value')}</p>
    </div>
  );
}

removeGlobalPersistStoreByKey

通过key移除全局持久化存储 返回值:布尔值

  • true 表示删除成功
  • false 表示删除失败
class FooterViewModel extends StoreViewModel<any> {
  removeLocalValue = () => {
    const removed = this.removeGlobalPersistStoreByKey('local_value');
    console.log('removed', removed);
  };

}
export { FooterViewModel };

hooks

useGlobalState

  • 类似useState hooks,获取全局 view 对应的state,仅view 适用
  • 响应来自updateGlobalStateByKey的更新

参数:

  • key:要获取的state对应的key
  • initialState:初始化state,类似useState默认值
import { useVM } from 'hooks-view-model';
import { AppViewModel } from './App.ViewModel.ts';

export default function App() {
 const {
    useGlobalState,
  } = useVM(AppViewModel, {});
  const { count } = useGlobalState(GLOBAL_KEYS.APP, { count: 0 });
  return <div>{count}</div>
}

useCurrentState

  • hooks,获取当前view 对应的state,仅view 适用
  • 响应来自updateCurrentState的更新

参数:

  • initialState:初始化state,类似useState默认值

用法:上同

useVM

  • hooks,实例化ViewModel,view通过调用useVM,可获取对应的ViewModel和StoreViewModel的所有public API;
  • 在组件挂载时执行mounted生命周期钩子;在组件卸载时 执行unmounted 生命周期钩子;
  • 当props 发生变化时,自动执行onPropsChanged方法
  • 将最新的props赋值给viewModel,viewModel 可通过this.props.xxx 获取最新的props

参数:

  • viewModel
  • props:view传递给viewModel的参数,ViewModel通过this.props访问
import  {  useVM } from 'hooks-view-model';

export default function Footer() {
  const {
    updateModalState,
    getGlobalStoreByKey,
    updateLocalValue,
    getGlobalPersistStoreByKey,
    removeLocalValue,
    useCurrentState
  } = useVM(FooterViewModel, {name: 'pefma', value: 'value'});
  const { count }  = useCurrentState({
    count: 0,
  });
  const [num, setNum] = useState(0);
  return (
    <div style={{ border: '1px solid red' }}>
      <p>count: {count}</p>
      <button onClick={() => updateCount(10)}>+</button>
      <button onClick={updateModalState}>updateGlobalStore</button>
      <button onClick={updateValue}>updateValue</button>
      <button onClick={updateLocalValue}>updateLocalValue</button>
      <button onClick={removeLocalValue}>removeLocalValue</button>
    </div>
  );
}

生命周期钩子

mounted

组件挂载的时候,ViewModel 会自动执行该方法,无需在view中引入useEffect执行相关生命周期api。 mounted相当于是viewModel的componentDidMount

mounted的好处就是api简洁,对于async函数的处理有天然的优势,可以对比一下useEffect hooks和mounted对于异步的处理:

function App() {
  useEffect(() => {
    (async () => {
      await Promise.all([fetchA(), fetchB()])
      await postC()
    })()
  }, [])
  return (<div>123</div>)
}
//App.ViewModel.ts
class App extends StoreViewModel {
  mounted = async () => {
    await Promise.all([fetchA(), fetchB()])
    await postC()
  }
}

unmounted

组件卸载的时候,ViewModel 会自动执行该方法

import StoreViewModel from 'hooks-view-model';

class HeaderViewModel extends StoreViewModel<any> {
  unmounted = () => {
    console.log('app will autorun when component unmounted')
  }
}
export { HeaderViewModel };

onPropsChanged

内置函数,props发生改变时才触发,相当于useDeepCompareEffect

接收新的props时触发,参数:props

// Counter.View.tsx
const Counter = (props: any) => {
	// 当传递给CounterViewModel的props发生变化时,onPropsChanged 会自动执行
  const { updateCount, useCurrentState } = useVM(CounterViewModel, {
    ...props,
  });
  const { count } = useCurrentState({ count: 0 });
  return (
    <div>
      <div>Count is {count}</div>
      <button onClick={() => updateCount(count + 1)}>count</button>
      <div></div>
    </div>
  );
};

export { Counter };
// Counter.ViewModel.ts
import StoreViewModel from 'hooks-view-model';

class CounterViewModel extends StoreViewModel<any> {
  constructor(props) {
    super(props);
  }
  updateCount = count => {
    this.updateCurrentState({
      count,
    });
  };
  getCount = () => {
    const count = this.getCurrentState();
    return count
  }
  updateModalState = () => {
    this.updateGlobalStore('modal_close', true);
  };
  updateLocalValue = () => {
    this.updateGlobalPersistStore('local_value', { name: 'huang', age: 123 });
  };
  removeLocalValue = () => {
    const removed = this.removeGlobalPersistStoreByKey('local_value');
    console.log('removed', removed);
  };
  onPropsChanged = (props: any) => {
    console.log('当props 发生变化时自动触发', props);
  };

}
export { CounterViewModel };