preface

I was recently asked by an interviewer: Can you implement a React Hooks program on Vue?

To be honest, the hardest part of this problem is how to make Vue and React render compatible. The worst part was that I was asked to write code on the spot. Luckily, my handwriting was so good that the interviewer was amazed. Yi? I seem to be bragging in the wrong place.

All right. I’m trying to tell you two things: don’t be a master on your resume. Otherwise, you are very likely to find yourself in the same situation as me. / Manual dog head

UseEffect analysis

useEffectExecution timing of

UseEffect can be considered as a combination of three life cycle functions componentDidMount, componentDidUpdate, and componentWillMount.

  • useEffect(()=>{}) < = > componentDidMount,componentDidUpdate
  • useEffect(()=>{},[]) < = > componentDidMount
  • useEffect(()=>()=>{}) < = > componentWillUnmount
// Let's see an example in action
function App4(props) {
  const [count, setCount] = useState(() = >{
    return props.count || 0; // execute when the component is first rendered
  });

  useEffect(() = >{
    console.log('I was mounted/updated by did'); 
  })

  useEffect(() = >{
    console.log('I was only mounted by did'); 
  },[])

  useEffect(() = >{
   return () = >{
     console.log('I'm unmounted')}})return <div>
    <span>{count}</span>

    <button onClick={()= >SetCount (count + 1)}> Click to give count + 1</button>
    <button onClick={()= >ReactDom. UnmountComponentAtNode (document. GetElementById (" root "))} > uninstall components</button>
    
  </div>
}
Copy the code

As you can see, the useEffect has three usage scenarios. Components are first mounted, updated, and unmounted. You can take this demo and run it, and actually do it. I’m not going to take screenshots here.

So useEffect is more than that, right? I’m going to stop here, in case MY friends beat me to death. 😂 😂 😂 😂

We know that useEffect can not only handle side effects directly, but also listen for state variables. If a side effect is just one thing, the purpose of listening for state variables is to control the occurrence of side effects caused by state variables from the state variables. Maybe it’s a little convoluted, but let’s have a to do list.

useEffect(() = >{
    console.log('Listen for empty [], meaning the current side effect will only be performed once'); 
 },[])
 
 useEffect(() = >{
    console.log([state0,state1,state2] [state0,state1,state2] [state0,state1,state2]); 
 },[state0,state1,state2]);
Copy the code

Oh. So let’s analyze it a little bit,

  • useEffectTwo parameters can be received, the first being callback (side effect handling callback). The second is a list, but not mandatory.
  • useEffectYou don’t have to structure the actual values
  • useEffectIt can also be called multiple times
  • useEffectOnce called, a listener is generated
  • useEffectCan be props or state

UseEffect implementation

  1. Verify the input parameter type of useEffect first.
  2. The second input parameter is checked for logical processing
  3. Once a listener is started, a copy of the listener state variable must be saved.
  4. The hardest piece of logic to implement: how to determine if a side effect needs to be performed.
// Last Dependency value
let prevDepsAry = [];
let effectIndex = 0;
function useEffect1(callback, depsAry) {
  if (Object.prototype.toString.call(callback) ! = ='[object Function]') throw new Error('useEffect. The first argument must be a function method')
  if (typeof depsAry === 'undefined') {
    // no state 
    callback();
  } else {
    // Determine whether depsAry is an array.
    if (Object.prototype.toString.call(depsAry) ! = ='[object Array]') throw new Error('useEffect. The next argument must be a Array')
    // Gets the last status value.
    let prevDeps = prevDepsAry[effectIndex];
    // Compare the current dependency value with the last dependency value. If there is any change, to call back fn.
    let hasChange = prevDeps ? depsAry.every((dep, index) = > dep === prevDeps[index]) === false : true
    if(hasChange) { callback(); } prevDepsAry[effectIndex] = depsAry; effectIndex++; }}function App11() {
  const [count, setCount] = useState1(0);
  const [name, setName] = useState1('fat');
  useEffect1(() = > {

    console.log(`hello ${count}`)
  }, [count])

  useEffect1(() = > {

    console.log(`hello ${name}`)
  }, [name])
  return <div>
    <span>{count}</span>
    <button onClick={()= >SetCount (count + 1)}> Click to give count + 1</button>
    <span>{name}</span>

    <button onClick={()= >SetName (' Cheating man ')}> setName</button>

  </div>
}
Copy the code

I was wrong, I wrote my very spicy chicken notes into Chinese 😭😭😭 please look again


// Last dependency value
let prevDepsAry = [];
let effectIndex = 0;

function useEffect(callback, depsAry) {
  // Check whether callback is a function
  if (Object.prototype.toString.call(callback) ! = ='[object Function]') throw new Error('useEffect function's first argument must be a function ');
  // Check if depsAry is passed
  if (typeof depsAry === 'undefined') {
    // No passing
    callback();
  } else {
    // Check if depsAry is an array
    if (Object.prototype.toString.call(depsAry) ! = ='[object Array]') throw new Error('The second argument to the useEffect function must be an array');
    // Get the last status value
    let prevDeps = prevDepsAry[effectIndex];
    // The current dependency value is compared to the last dependency value. If there is any change, call callback
    let hasChanged = prevDeps ? depsAry.every((dep, index) = > dep === prevDeps[index]) === false : true;
    // Check whether the value has changed
    if (hasChanged) {
      callback();
    }
    // Synchronize dependent values
    prevDepsAry[effectIndex] = depsAry;
    effectIndex++; // In the render method effectIndex is reset to 0;}}Copy the code

All right, go ahead and try it.

Write useReducer by the way. Because it’s easy.

Go to the code and you’ll see why it’s easy. 😂 😂 😂 😂

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

function App() {
  function reducer (state, action) {
    switch (action.type) {
      case 'increment':
        return state + 1;
      case 'decrement':
        return state - 1;
      default:
        returnstate; }}const [count, dispatch] = useReducer(reducer, 0);
  return <div>
    {count}
    <button onClick={()= > dispatch({type: 'increment'})}>+1</button>
    <button onClick={()= > dispatch({type: 'decrement'})}>-1</button>
  </div>;
}
Copy the code

Yeah! You read that right, the dispatches here are very similar to the dispatches in Redux. But the point is, useState is used here! For the implementation of useState, look back at useState

So it’s very simple 😂

The subtotal

A simple cut? Is the wave finally getting easier? Don’t say what I write is difficult 😢😢😢😢😢

The next part will probably include memo and useMemo. If you write it, you have to write Fiber first. The memo is intended to affect the effectTags on the Fiber node (add, delete, modify).

Don’t worry. I also implemented a set of Fiber. If the requirements are higher, then I have to implement the React coordination layer and the scheduling layer 🤦♂️ 🤦♂️ port