redux

84 阅读4分钟

获取不到最新值

问题复现


function createS() {
    let v = 1
    const getV = () => v
    const setV = (x: number) => v=x

    return {
        v, // v的值在return的一瞬间就定死了
        getV,
        setV
    }
}
const s = createS()
s.setV(222)
console.log('***', s.v)	// 1
console.log('+++', s.getV())	// 222

function createS() {
    let v = 1
    const getV = () => v
    const setV = (x: number) => v=x

    return {
        v,
        getV,
        setV
    }
}
const s = createS()
s.v = 999
console.log('***', s.v)	// 999
console.log('+++', s.getV())	// 1

const o = {
    v: 1,
    getV() {
        return this.v
    },
    setV(x: number) {
        this.v = x
    }
}

o.setV(333)
console.log(o.v)		// 333
console.log(o.getV());	// 333

原因:

  • 修改api.v 不会影响到 let v
  • 函数执行完毕后, 调用setV, 不会影响到api.v
function createS() {
    let v = 1
    const getV = () => v
    const setV = (x: number) => v=x

    let api = {
        v,
        getV,
        setV
    }
    
    return api
}
const s = createS()
s.v = 999
console.log('***', s.v)	// 999
console.log('+++', s.getV())	// 1

creatStore

export const createStore = <S, A>(reducer: Function) => {
    let state: S = reducer()
    let fns: Function[] = []
    const getState = () => state

    const subscrible = (refresh: Function) => {
        fns.push(refresh)
    }

    const dispatch = (action: A) => {
        state = reducer(state, action) // 更新数据
        fns.forEach(refresh => refresh()) // 触发渲染
    }

    return {
        getState,
        subscrible,
        dispatch
    }
}

增加一个取消订阅

export const createStore = <S, A>(reducer: Function) => {
    let state: S = reducer()
    let fns: Function[] = []
    const getState = () => state

    const subscrible = (refresh: Function) => {
        fns.push(refresh)
        return () => {
            const index = fns.indexOf(refresh);
            fns.splice(index, 1);
        }
    }

    const dispatch = (action: A) => {
        state = reducer(state, action) // 更新数据
        fns.forEach(refresh => refresh()) // 触发渲染
    }

    return {
        getState,
        subscrible,
        dispatch
    }
}

react-redux

import { FC, createContext, useContext, useEffect, useState } from "react";
import { createStore } from "./redux";
import { useMounted } from "./useMount";

type S = ReturnType<typeof createStore> // string
const StoreCtx = createContext<Partial<S>>({})

interface IProvider {
    children: any
    store: S
}

export const Provider: FC<IProvider> = ({store, children}) => {
    return <StoreCtx.Provider value={store} >{children}</StoreCtx.Provider>
}

export const useDispatch = () => {
    const store = useContext(StoreCtx) as S
    return store.dispatch
}

export const useSelect = (cb: Function) => {
    const store = useContext(StoreCtx) as S
    const [_, render] = useState({})
    
    const getValue = () => {
        const state = store.getState()
        const value = cb(state)
        return value
    }
    
    useMounted(() => {
        const unSub = store.subscrible(renderPro) // 订阅渲染函数
        return () => unSub() // 取消订阅渲染函数
    })


    return getValue()
}

增加功能: 如果value没有变化就组件就不更新

import { FC, createContext, useContext, useEffect, useState } from "react";
import { createStore } from "./redux";
import { useMounted } from "./useMount";

type S = ReturnType<typeof createStore> // string
const StoreCtx = createContext<Partial<S>>({})

interface IProvider {
    children: any
    store: S
}

export const Provider: FC<IProvider> = ({store, children}) => {
    return <StoreCtx.Provider value={store} >{children}</StoreCtx.Provider>
}

export const useDispatch = () => {
    const store = useContext(StoreCtx) as S
    return store.dispatch
}

export const useSelect = (cb: Function) => {
    const store = useContext(StoreCtx) as S
    const [_, render] = useState({})
    const getValue = () => {
        const state = store.getState()
        const value = cb(state)
        return value
    }
    
    const value = getValue()
    // 如果value没有变化就不重新渲染
    const renderPro = () => {
        // useSelect执行前的value, render先执行, 触发渲染, useSelect才会执行
        const newValue = getValue() 
        // useSelect执行时的value
        const oldValue = value 
        if(oldValue !== newValue) render({})
    }
    useMounted(() => {
        const unSub = store.subscrible(renderPro) // 订阅渲染函数
        return () => unSub() // 取消订阅渲染函数
    })


    return value
}

中间件

  • 中间件就是函数合成
  • 函数合成又叫洋葱模型/中间件
  • 函数合成可以帮我们把一个复杂的函数变成多个简单的函数

写法1

简化版

function run(middlewares) {
  function dispatch(index) {
    const fn = middlewares[index];
    if (!fn) return;
    const next = () => dispatch(index + 1)
    fn(next);
  }
  dispatch(0);
}

const middlewares = [
  (next) => {
    console.log("start-1");
    next();//此时 next = () => dispatch(1)
    console.log("end-1");
  },
  (next) => {
    console.log("start-2");
    next();//此时 next = () => dispatch(2)
    console.log("end-2");
  },
  (next) => {
    console.log("start-3");
    next();//此时 next = () => dispatch(3)
    console.log("end-3");
  }
];

run(middlewares);

传参版:

const run2 = (middlewares) => (arg) => {
    const dispatch = (index) => {
        const fn = middlewares[index];
        if (!fn) return;
        const next = () => dispatch(index + 1)
        fn(next)(arg);
    };
    dispatch(0);
};

const middlewares2 = [
    (next) => (arg) => {
        console.log(arg, "start-1");
        next();
        console.log(arg, "end-1");
    },
    (next) => (arg) => {
        console.log(arg, "start-2");
        next();
        console.log(arg, "end-2");
    },
    (next) => (arg) => {
        console.log(arg, "start-3");
        next();
        console.log(arg, "end-3");
    }
];

run2(middlewares2)("context");

缺点

  • 如果arg是基础数据类型
  • arg将无法被修改

原因

let a = 1

function fn(a) {
    a = 2
}

fn(a)
console.log(a) // 1
let a = {age: 1}

function fn(a) {
    a.age = 2
}

fn(a)
console.log(a) // {age: 2}

完成版

arg值可修改

const middlewares3 = [
    (next) => (ctx) => {
        console.log(ctx, "start-1");
        next(ctx+1);
        console.log(ctx, "end-1");
    },
    (next) => (ctx) => {
        console.log(ctx, "start-2");
        next(ctx+10);
        console.log(ctx, "end-2");
    },
    (next) => (ctx) => {
        console.log(ctx, "start-3");
        next(ctx);
        console.log(ctx, "end-3");
    }
];

const run3 = (midwares: Function[]) => (ctx: any) => {
    /**
    * @param i 选择midwares第i个函数执行
    * @param arg 第i个函数执行时需要的参数
    */
    const chooseFnRun = (i: number, ctx: any) => {
        const fn = midwares[i]
        if (!fn) return
        const next = (ctx: any) => chooseFnRun(i + 1, ctx)
        fn(next)(ctx)
    }
    chooseFnRun(0, ctx)
}

run3(middlewares3)(100)
// 打印:
// 100 start-1
// 101 start-2
// 111 start-3
// 111 end-3
// 101 end-2
// 100 end-1

写法2

无参版

const tasks = [
  (next) => {
    console.log("start-1");
    next();
    console.log("end-1");
  },
  (next) => {
    console.log("start-2");
    next();
    console.log("end-2");
  },
  (next) => {
    console.log("start-3");
    next();
    console.log("end-3");
  }
];

let curIndex = 0

const runTask = () => {
  const task = tasks[curIndex]
  if(task) task(next)
  else curIndex=0
}

const next = () => {
  curIndex++
  runTask()
}

runTask()

传参版

const tasks = [
    (next) => (ctx) => {
        console.log(ctx, "start-1");
        next(ctx+1);
        console.log(ctx, "end-1");
    },
    (next) => (ctx) => {
        console.log(ctx, "start-2");
        next(ctx+10);
        console.log(ctx, "end-2");
    },
    (next) => (ctx) => {
        console.log(ctx, "start-3");
        next(ctx);
        console.log(ctx, "end-3");
    }
];

let curIndex = 0

const runTask = (ctx) => {
  const task = tasks[curIndex]
  if(task) task(next)(ctx)
  else curIndex=0
}

const next = (ctx: any) => {
  curIndex++
  runTask(ctx)
}

runTask(10)

优化

const tasks = [
    (next) => (ctx) => {
        console.log(ctx, "start-1");
        next(ctx+1);
        console.log(ctx, "end-1");
    },
    (next) => (ctx) => {
        console.log(ctx, "start-2");
        next(ctx+10);
        console.log(ctx, "end-2");
    },
    (next) => (ctx) => {
        console.log(ctx, "start-3");
        next(ctx);
        console.log(ctx, "end-3");
    }
];

function run(tasks, ctx) {
  let curIndex = 0
  runTask(ctx)

  function runTask(ctx) {
    const task = tasks[curIndex]
    if(!task) return
    task(next)(ctx)
  }

  function next(ctx) {
    curIndex++
    runTask(ctx)
  }
}

run(tasks, 10)

写法3

基础版

const chain = [
  (args) => {
    console.log(args, "---f1");
    return args;
  },
  (args) => {
    console.log(args, "---f2");
    return args;
  },
  (args) => {
    console.log(args, "---f3");
    return args;
  }
];

const compose3 = (...fns) => {
  if (fns.length === 0) return (arg) => arg;
  if (fns.length === 1) return fns[0];
  return fns.reduce((a, b) => (...arg) => a(b(...arg)));
};

compose3(...chain)("aaa");

// 打印
// ---f3
// ---f2
// ---f1

加强版


const f = [
  (next) => (arg) => {
    console.log(arg, "--- start 1");
    next(arg);
    console.log(arg, "--- end 1");
  },
  (next) => (arg) => {
    console.log(arg, "--- start 2");
    next(arg);
    console.log(arg, "--- end 2");
  },
  (next) => (arg) => {
    console.log(arg, "--- start 3");
    next(arg);
    console.log(arg, "--- end 3");
  }
];
// compose4函数就定义了两个函数, a和b
// let a = (...arg) => f[0](f[1](...arg))
// let b = (...arg) => a(f[2](...arg))
const compose4 = (...fns) => {
  if (fns.length === 0) return (arg) => arg;
  if (fns.length === 1) return fns[0];
  return fns.reduce((a, b) => (...arg) => a(b(...arg)));
};

//相当于执行:  b(() => console.log("aaa"))("参数");
compose4(...f)(() => console.log("aaa"))("参数");


// 打印
// 参数--- start 1
// 参数--- start 2
// 参数--- start 3
// aaa
// 参数--- end 3
// 参数--- end 2
// 参数--- end 1

写法4

const mdws = [
  (next) => (arg) => {
    console.log(arg, "start-1", "写法二");
    next(arg);
    console.log(arg, "end-1", "写法二");
  },
  (next) => (arg) => {
    console.log(arg, "start-2", "写法二");
    next(arg);
    console.log(arg, "end-2", "写法二");
  },
  (next) => (arg) => {
    console.log(arg, "start-3", "写法二");
    next(arg);
    console.log(arg, "end-3", "写法二");
  }
];

const compose = (mdws) => (fn) => {
  mdws.reverse();
  mdws.map((mdw) => (fn = mdw(fn)));
  return fn;
};

compose(mdws)((arg) => console.log(arg, "center--- 写法二"))("jack");