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

Write a set of Thoughts before React Hooks

So, how do you describe itReact Hooks?

  • Enhancements to functional components. Let functional components do what class components do.
  • Let functional components store state
  • Can have the ability to deal with side effects
  • Let developers implement the same functionality without using class components

So, what’s the downside of class components? Class component deficiencies (React HooksProblems to be solved)

  1. Lack of logical reuse mechanism

    • In order to reuse the logic to add no actual rendering of the components, increase the nesting level of components, very bloated.
    • The difficulty of debugging is increased and the efficiency of actual operation is reduced.
  2. Class components can often become complex and difficult to maintain.

    • Split a coherent set of business logic into multiple lifecycle functions
    • There are multiple unwanted business logic in a lifecycle function.
    • Class member methods are not guaranteedthisCorrectness of pointing
    • When we bind events to an element, when we want to change the state in an event handler, we usually need to change the state inside the functionthisPointing is not as good asthisJust point toundefined.
    • The solution isbindOr the way functions are nested within functionsthisPointing to.

** What are the salient features of React Hooks? 支那

Feature points React Hooks Class
Usage scenarios Inside the function There’s no limit. You can write class anywhere, right
Change the states const [state,setState] = useState(initState) this.setState
The life cycle Does not exist, but can be simulated by Effect Hooks There are strict life cycle functions
State storage State Hooks State variables are stored in this.state
Status monitoring unit State variables are units Without this concept, monitor code can only be written in the lifecycle function

UseState analysis and implementation

Start with the official useState chestnuts.

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable called "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={()= > setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
Copy the code

Well, if we change it again. For example:

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable called "count"
  const [count, setCount] = useState([0]);
  const [name, setName] = useState('fat');
  const [meizhi, setMeiZhi] = useState(() = >'bosses');
  return (
    <div>
      <p>You clicked {count} times</p>
      <p>Call me {name}</p>
      <p>{name} is meizhi?</p>
      <button onClick={()= >{ setCount([count[0] + 1]); SetName (' Fat man is a cheating and philandering man '); SetMeiZhi ((preMeizhi) = > ` ladies' ${preMeizhi} `); }}> Click me</button>
    </div>
  );
}
Copy the code

We can summarize the use of useState.

  • Accepts incoming arguments of any type, which can be arrays, objects, functions, and so on.
  • It can be called multiple times, each time deconstructing a different state variable.
  • setStateCan also bePass in the callback function. The change callback is automatically passed to the current corresponding state variable.
  • The structured state variables and the methods used to change them are bound 1-to-1.
  • callsetStateAfter, to rerenderFunction component

Start to write

// import React from 'react';
import ReactDOM from 'react-dom';
// Write render first, ready.
function render () {
  ReactDOM.render(<App />.document.getElementById('root'));
}

// The App component is also ready

function App() {
  const [count, setCount] = useState([0]);
  const [name, setName] = useState('fat');
  const [meizhi, setMeiZhi] = useState(() = >'bosses');
  return (
    <div>
      <p>You clicked {count} times</p>
      <p>Call me {name}</p>
      <p>{name} is meizhi?</p>
      <button onClick={()= >{ setCount([count[0] + 1]); SetName (' Fat man is a cheating and philandering man '); // setMeiZhi((preMeizhi)=> 'ladies ${preMeizhi}'); }}> Click me</button>
    </div>
  );
}

// Start with useState(...) start
let state;
function useState(initState) {
  if(! state) state = initState;// If state is undefined, useState is called for the first time. The page has not been re-rendered
  let setState = (newState) = > {
    if(state ! == newState) { state = newState; } render() }return [state, setState]
}


Copy the code

Click on theClick mebefore

Click on theClick meafter

The problem is obvious because there is only one state variable in the code, and each call to useState returns state. What we need to do is structure a different [state, setState] each time we call useState. Come again.

let state = []; // Store state variables
let setters = []; // The method to store the changed state variable
let stateIndex = 0; // Bind state[...] And setters [...]. That is, the state variable and the method to change it must have a 1-to-1 relationship.


function useState (initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState; / / in the same way. This is putting the state in an array.
  setters.push(createSetter()); // Create a setState method based on the stateIndex and push it into the setters array.
  let value = state[stateIndex]; // Take the value from an array of state variables
  let setter = setters[stateIndex]; // Retrieve the corresponding setState from the setState method array
 stateIndex++
  return [value, setter]; // returns for the caller to deconstruct.
}

function createSetter () {
  return (newState) = > {
    state[stateIndex]= newState;
    render();
  };
}
Copy the code

Look at the result, it is:

Only the first row has changed. Only the value of the first state variable is rerendered. The stateIndex is already passed to the createSetter method in the code. Look carefully, and you’ll find a problem. When you call setState multiple times, the stateIndex is not retained.

This is solved with closures.

let state = []; // Store state variables
let setters = []; // The method to store the changed state variable
let stateIndex = 0; // Bind state[...] And setters [...]. That is, the state variable and the method to change it must have a 1-to-1 relationship.


function useState (initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState; / / in the same way. This is putting the state in an array.
  setters.push(createSetter(stateIndex)); // Create a setState method based on the stateIndex and push it into the setters array.
  let value = state[stateIndex]; // Take the value from an array of state variables
  let setter = setters[stateIndex]; // Retrieve the corresponding setState from the setState method array
 stateIndex++
  return [value, setter]; // returns for the caller to deconstruct.
}

function createSetter (index) {
 // Keep the index passed in by returning a new method. So here's the closure
  return function (newState) { state[index] = newState; render (); }}Copy the code

And the results:

Use the useState and setState argument type function once more. The complete code is as follows:


let state = [];
let setters = [];
let stateIndex = 0;

const getStateByFn = (v, params) = > {
  if (typeof v === 'function') {
    const _newState = v(params);
    if(! _newState)throw 'You must be return state'
    return _newState
  }
  return v
}

function createSetter(index) {
  return function (newState) { state[index] = getStateByFn(newState,state[index]) render(); }}function useState(initialState) {
  state[stateIndex] = state[stateIndex] ? getStateByFn(state[stateIndex]) : getStateByFn(initialState);
  setters.push(createSetter(stateIndex));
  let value = state[stateIndex];
  let setter = setters[stateIndex];
  stateIndex++;
  return [value, setter];
}

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

function App() {
  const [count, setCount] = useState([0]);
  const [name, setName] = useState('fat');
  const [meizhi, setMeiZhi] = useState(() = > 'bosses');
  return (
    <div>
      <p>You clicked {count} times</p>
      <p>Call me {name}</p>
      <p>{name} is {meizhi}?</p>
      <button onClick={()= >{ console.log('count:', count, 'name:', name, 'meizhi:', meizhi) setCount([count[0] + 1]); SetName (' Fat man is a cheating and philandering man '); SetMeiZhi ((preMeizhi) => 'dress ${preMeizhi}'); }}> Click me</button>
    </div>
  );
}
Copy the code

Click on theClick meThe results are as follows:

To be continued

It’s very detailed. If you have any questions, please leave a comment.

The implementation of useEffect will be explained in the next section.