likes
comments
collection
share

useState,useEffect,useReduce手写实现

作者站长头像
站长
· 阅读数 21
// ----------useState-----------------
let state = []; // 对应值
let setters = []; // 对应设置状态值方法
let stateIndex = 0;

function createSetter(index) {
  return function (newState) {
    state[index] = newState;
    // 状态更改后重新渲染视图
    render();
  };
}

function useState(initialState) {
  // state = state ? state : initialState;
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState;
  // function setState(newState) {
  //   console.log(newState);
  //   state = newState;
  //   // 状态更改后重新渲染视图
  //   render();
  // }
  setters.push(createSetter(stateIndex));
  let value = state[stateIndex];
  let setter = setters[stateIndex];
  stateIndex++;
  return [value, setter];
}

// --------------useEffect-----------------
// 上一次的依赖值
let prevDepsAry = []; // 结果应该为一个二位数组
let effectIndex = 0;
function useEffect(callback, depsAry) {
  // 判断callback是不是函数
  if (Object.prototype.toString.call(callback) !== "[object Function]") {
    throw new Error("useEffect函数的第一个参数必须是函数");
  }
  // 判断depsAry有没有被传递
  if (typeof depsAry === "undefined") {
    // 没有传递
    callback();
  } else {
    // 判断depsAry是不是数组
    if (Object.prototype.toString.call(depsAry) !== "[object Array]") {
      throw new Error("useEffect函数的第二个参数必须是数组");
    }
    // 获取上一次的状态值
    let prevDeps = prevDepsAry[effectIndex];
    // 将当前的依赖值和上一次的依赖值做对比 如果有变化 调用callback
    let hasChange = prevDeps
      ? depsAry.every((dep, index) => dep === prevDeps[index]) === false
      : true;

    // 判断值是否有变化
    if (hasChange) {
      callback();
    }
    // 同步依赖值
    prevDepsAry[effectIndex] = depsAry;
    effectIndex++;
  }
}

// -------------useReduce-------------
    function useReducer(reducer, initialState) {
    const [state, setState] = useState(initialState);
    function dispatch(action) {
        const newState = reducer(state, action);
        setState(newState);
    }
    return [state, dispatch];
    }

    function reducer(state, action) {
    switch (action.type) {
        case "increment":
        return state + 1;

        case "decrement":
        return state - 1;
        default:
        return state;
    }
    }
    const [count, dispatch] = useReducer(reducer, 0);


function render() {
  stateIndex = 0;
  effectIndex = 0;
  ReactDOM.render(<App />, document.getElementById("root"));
}

类似于antd的一些组件库的组件,经常需要绑定一些value值和onChange事件。可以封装一个通用方法

function useUpdateInput(initvalue) {
  const [value, setValue] = useState(initvalue);
  return {
    value,
    onChange: (e) => setValue(e.target.value),
  };
}
const username = useUpdateInput("");

Lazy使用

const LazyComponent = React.lazy(() => import('./test.js'))

export default function Index(){
   return <Suspense fallback={<div>loading...</div>} >
       <LazyComponent />
   </Suspense>
}

function Test(){
  return <div className="demo"  >hello world</div>
}
const LazyComponent =  React.lazy(()=> new Promise((resolve)=>{
  setTimeout(()=>{
      resolve({
          default: ()=> <Test />
      })
  },2000)
}))

react中lazy实现

function lazy(ctor){
    return {
         $$typeof: REACT_LAZY_TYPE,
         _payload:{
            _status: -1,  //初始化状态
            _result: ctor,
         },
         _init:function(payload){
             if(payload._status===-1){ /* 第一次执行会走这里  */
                const ctor = payload._result;
                const thenable = ctor();
                payload._status = Pending;
                payload._result = thenable;
                thenable.then((moduleObject)=>{
                    const defaultExport = moduleObject.default;
                    resolved._status = Resolved; // 1 成功状态
                    resolved._result = defaultExport;/* defaultExport 为我们动态加载的组件本身  */ 
                })
             }
            if(payload._status === Resolved){ // 成功状态
                return payload._result;
            }
            else {  //第一次会抛出Promise异常给Suspense
                throw payload._result; 
            }
         }
    }
}

Suspense让数据获取库与 React 紧密整合。如果一个数据请求库实现了对 Suspense 的支持,那么,在 React 中使用 Suspense 将会是自然不过的事。Suspense能够自由的展现,请求中的加载效果。能让视图加载有更主动的控制权。Suspense能够让请求数据到渲染更流畅灵活,我们不用在componentDidMount请求数据,再次触发render,一切交给Suspense解决,一气呵成。