乐趣区

关于react.js:useStateuseEffectuseReduce手写实现

// ----------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 解决,零打碎敲。

退出移动版