React hooks source code feels a bit complicated. Now I have to learn usage and write a simple version of hooks. But before we do that, we want to know a few more nouns

Stateful components vs. non-stateful components

It is recommended to read the difference between stateful and stateless components

State of the component

Stateless component

No state and lifecycle functions of its own. Accept a props, just pure props presentation component, no state updates involved

export const Button=(props)=>{  
    return( 
        <Button className={this.props.className)/>
    )
}
Copy the code

State of the component

Has its own state and lifecycle functions. For example, the following internal state can be changed by external props changes

export class Header extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
          title: ' '
 }  };  render() {  return (  <Header title={this.state.title}/>  )  } } Copy the code

When are stateful and stateless components used

When the update does not involve state, stateless component is used, which has high efficiency and high reusability. And vice versa.

Controlled components vs. uncontrolled components

Controlled-vs-uncontrolled – elsies-react is recommended

The controlled components

Uncontrolled component

The form data is processed by the DOM node, in other words, using the ref to get the value, as in

class Form extends React.Component {
  constructor(props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.input = React.createRef();
 }   handleInputChange() {  alert( this.input.current.value);  }   render() {  return (  <form onSubmit={this.handleSubmit}>  <label>  Name:  <input type="text" ref={this.input} onChange={this.handleInputChange}/>  </label>  </form>  );  } } Copy the code

The controlled components

A controlled component accepts the current value as a prop and can change the value through callbacks, such as

class Form extends React.Component {
  constructor() {
    super();
    this.state = {
      name: ' '. };  }   handleInputChange = (event) => {  this.setState({ name: event.target.value });  };   render() {  return (  <div>  <input  type="text"  value={this.state.name}  onChange={this.handleInputChange}  />  </div>  );  } } Copy the code

When are controlled and uncontrolled components used

Characteristics of the Uncontrolled component The controlled components
One-time acquisition (such as Submit) Square root Square root
Validation at submission Square root Square root
Real-time validation x Square root
Conditionally disable the Submit button x Square root
Mandatory input format x Square root
Multiple inputs to a single data set x Square root
Dynamic input x Square root

useState

usage

 const [number, setNumber] = useState(0);
  return (
    <div>
      <div>{number}</div>
<button onClick={() => setNumber(number)}> </button> </div>  ); Copy the code

It looks simple. Returns a state and a function to update state. Every time the state changes, join the render update queue, that is, the view changes. Each time the function that updates state returns the last updated value. How do I join the update queue? How do I return the last updated value

A simple version of useState

let index = 0;
let hookState = [];
function useState(initalState){
  let currentIndex = index;
  hookState[currentIndex] = initalState;
 function setState(newState){ HookState [currentIndex] = newState // Join update queueRender () / / rendering } // Returns the value of the last updatereturn [ hookState[index++],setState] }  function Counter() {  const [number, setNumber] = useStates(0);  return (  <div>  <div>{number}</div> <button onClick={() => setNumber(number + 1)}> </div>  ); }  function render() { The index = 0; // Index = 0 after each render ReactDOM.render(  <React.StrictMode>  <Counter></Counter>  </React.StrictMode>,  document.getElementById("root")  ); } render(); Copy the code

The source code

type BasicStateAction<S> = (S => S) | S;
type Dispatch<A> = A => void;

function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
  return typeof action === 'function' ? action(state) : action;
}   export function useState<S>(  initialState: (() => S) | S, ): [S, Dispatch<BasicStateAction<S>>] {  return useReducer(  basicStateReducer,  // useReducer has a special case to support lazy useState initializers  (initialState: any),  ); } Copy the code

The above source code identifies incoming and return parameters in Typescript. UseState is the syntax sugar of useReducer. To see the source code for useState, take a look at useRecuder

useRecuder

Basic usage

grammar

const [state, dispatch] = useReducer(reducer, initialArg, init);
Copy the code

Reducer and Redux reducer have the same usage, receiving state and action and returning state and currently matched dispatch

The sample

import React, { useReducer } from "react";
import ReactDOM from "react-dom";

const initialState = { count: 0 };

function counterReducer(state, action) {  switch (action.type) {  case "increment":  return { count: state.count + 1 };  case "decrement":  return { count: state.count - 1 };  default:  throw new Error();  } }  function Counter() {  let [state, dispatch] = useReducer(counterReducer, initialState);  console.log(state)  return (  <div>  <p>{state.count}</p>  <button onClick={() => dispatch({ type: "increment" })}>+</button>  </div>  ); } function render() {  ReactDOM.render(<Counter />, document.getElementById("root")); } render(); Copy the code

Use useReducer instead of useState when you have a lot of states.

A simple version of useReducer

// The following is a simple versionlet hookState = [];
let hookIndex = 0;

function useReducer(reducer, initialState) {
 hookState[hookIndex] = hookState[hookIndex] || initialState;  let currentIndex = hookIndex;  function dispatch(action) {  console.log(action); // Distinguish between useState and useReducer cases hookState[currentIndex] = reducer  ? reducer(hookState[currentIndex], action)  : action;  render();  }  return [hookState[hookIndex++], dispatch]; }  function useState(initialState) {  return useReducer(null, initialState); }  // function Counter1() { // let [number, setNumber] = useState(0); // return ( // <div> // <p>{number}</p> // <button onClick={() => setNumber(number + 1)}>+</button> // </div> // ); // }  function Counter() {  let [state, dispatch] = useReducer(counterReducer, initialState);  return (  <div>  <p>{state.count}</p>  <button onClick={() => dispatch({ type: "decrement" })}>+</button>  </div>  ); }  function render() {  hookIndex = 0;  ReactDOM.render(<Counter1 />, document.getElementById("root")); }  render();  Copy the code

The source code

Source code based on Fiber, a little complex. The source code

The follow-up plan

The next plan, useCallback and useMemo, is to step on the pit most, because a careless, easy to cause a loop call