State management

The paper

  • Sharing state between multiple components and modules is the most common development scenario, and there are countless scenarios, such as global user state, modification of user information, global response changes, and so on.

Common solutions

  • Based on the event monitoring mechanism, the simple scheme uses callback to pass parameters and multiple subscriptions to realize data transfer. For example, the officially recommended Mitt event library. The advantages of simple data event communication can be satisfied, while the disadvantages are that the code readability of callback writing decreases with the change of data complexity, and the overall use experience is also very simple. The specific implementation is as follows:
// Define the composite API event stream
const $emitter = mitt()
/ * * *@name: useOnChange
 * @msg: Listen for the event emit */
export function useOnChange<T extends Function> (fun: T) {
  $emitter.on(EventsEnum.CHANGE, itemMessage= > {
    fun(itemMessage)
  })
}
/ * * *@name: useChange
 * @msg: Triggers the event emit *@param {*}* /
export function useChange(itemMessage: number) {
  $emitter.emit(EventsEnum.CHANGE, itemMessage)
}
Copy the code
// Component A triggers the event to send data
export default defineComponent({
  name: 'A'.setup() {
   // Component A sends data
   const handlerClick = (item) = >{
     // Use the composite API to send data
     useChange(1)}}})// component B listens for events to get data
export default defineComponent({
  name: 'B'.setup() {
    // Get data in callback
   useOnChange((mes) = >{
    console.log(mes)
   })
  }
 })
 
// the C component listens for events to get data
export default defineComponent({
  name: 'C'.setup() {
   // Get data in callback
   useOnChange((mes) = >{
     console.log(mes)
   })
  }
 })
Copy the code
  • Some simple practices based on VUE3 responsive official
const store = {
  debug: true.state: Vue.reactive({
    message: 'Hello! '
  }),

  setMessageAction(newValue) {
    if (this.debug) {
      console.log('setMessageAction triggered with', newValue)
    }

    this.state.message = newValue
  },

  clearMessageAction() {
    if (this.debug) {
      console.log('clearMessageAction triggered')}this.state.message = ' '}}Copy the code
const appA = Vue.createApp({
  data() {
    return {
      privateState: {},
      sharedState: store.state
    }
  },
  mounted() {
    store.setMessageAction('Goodbye! ')
  }
}).mount('#app-a')

const appB = Vue.createApp({
  data() {
    return {
      privateState: {},
      sharedState: store.state
    }
  }
}).mount('#app-b')
Copy the code
  • Well-known state management libraries Redux,Flux,Vuex, these are excellent third-party libraries

Why are you doing this when there’s a Vuex?

  • $message: this.$message: this.$message: this. What’s the difference between working on one project and working on ten? Since the new opportunity to write vuE3 components, vuE3 state management?
  • Business and scenarios are relatively simple at the beginning of the project, with no need to record changes, save state snapshots, history rollback/time travel, so why not do state management yourself?
  • Core implementation functions: state modification single data flow, state change global data response, code conventions, think about how to solve these three problems?

Implementation approach

  • Single data stream, Readonly
  • State change data response, combined API and responsivity
  • Code constraints use TS for interface conventions

Some realization of other great gods

  • Using the dojo.provide
  • There are some ideas based on Reactive and so on

Standing on the shoulders of giants

  • Reduer ideas based on vuE3 encapsulation are also done themselves
  1. Basic implementation
/* * @description :Reducer * @version: 1.0.0 * @author: Wuwen-zhou * @Date: 2021-02-26 13:45:46 * @lasteditors: Wu Wenzhou * @lasteditTime: 2021-03-02 14:52:33 */
import { readonly, ref } from 'vue'
// Global cache
const map = new WeakMap(a)export function useModel(hook: Function) {
  if(! map.get(hook)) {const ans = hook()
    map.set(hook, ans)
  }
  return map.get(hook)
}
export function useReducer(reducer: Function, initialState = {}) {
  const state = ref(initialState)
  const dispatch = <T>(action: T) = > {
    state.value = reducer(action, state.value)
  }
  return {
    state: readonly(state),
    dispatch,
  }
}
export function useStore(reducer: Function, initialState? :any) {
  return useReducer(reducer, initialState)
}
Copy the code

2. Implement small Reduer

 /* * @description: XXX global status * @version: 1.0.0 * @author: @date: 2021-02-26 13:53:09 * @lasteditors: Wu Wenzhou * @lasteditTime: 2021-03-20 16:10:59 */
import { Ref } from 'vue'
import { useModel, useReducer } from './reducer'
// State interface
export interface State {
   oo: string.xx: string.cc: string,}// Behavior interface
export interface Action {
  type: 'changeOO' | 'changeXX' | 'changeCC'/ / specified action
  payload: State
}
// Composite functions use the state interface
export type StateType = Readonly<Ref<State>>
// Example Interface
interface Redux {
  state: StateType
  // This is not a comment, just such syntax mark when not recognized, to ensure elegance, the actual use of open comments
  //dispatch: <T extends Action>(action: T) => void
}
// Status changes
function reducer(action: Action, state: State) {
  switch (action.type) {
    case 'changeOO':
     state.oo = action.payload.oo
     break
    case 'changeXX':
     state.xx = action.payload.xx
     break
    case 'changeCC':
    state.cc = action.payload.cc
     break
  }
  return { ...state }
}
// Initialization state
function useStore() {
  const initialState = {
    oo: 'oo'.xx: 'xx'.cc: 'cc',}return useReducer(reducer, initialState)
}
// Composite API functions can be called anywhere by any component
export function useXXXRedux() {
  const redux: Redux = useModel(useStore)
  return redux
}
Copy the code

3. Call implementation, within any component, or within any composite API


export default defineComponent({
  name: 'D'.setup() {
   // Get data in callback
    const { state:xxState,dispatch } = useXXXRedux()
   // Listen for state changes
    watch(xxState, state= >{})// Trigger a state change
   dispatch({type:"changeOO", {payload: {oo:"iii"}}})})Copy the code

conclusion

  • Disadvantages: Logging changes, saving state snapshots, history rollback/time travel appeals these are missing
  • Advantages: The overall code is straightforward, non-invasive
  • Proficiency with third-party libraries is a basic quality for a developer