class
的情况下使用 state
以及其他的 React 特性import React from './react';
import ReactDOM from './react-dom';
function App(){
const[number,setNumber]=React.useState(0);
let handleClick = ()=> setNumber(number+1)
return (
<div>
<p>{number}</p>
<button onClick={handleClick}>+</button>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
src\react-dom.js
+let hookStates = [];
+let hookIndex = 0;
+let scheduleUpdate;
+function render(vdom, container) {
+ mount(vdom,container);
+ scheduleUpdate = ()=>{
+ hookIndex = 0;
+ compareTwoVdom(container,vdom,vdom);
+ }
+}
+export function useState(initialState){
+ hookStates[hookIndex] = hookStates[hookIndex]||initialState;
+ let currentIndex = hookIndex;
+ function setState(newState){
+ if(typeof newState === 'function') newState=newState(hookStates[currentIndex]);
+ hookStates[currentIndex]=newState;
+ scheduleUpdate();
+ }
+ return [hookStates[hookIndex++],setState];
+}
src\react.js
+import {useState} from './react-dom';
const React = {
createElement,
Component,
PureComponent,
createRef,
createContext,
cloneElement,
memo,
+ useState
};
export default React;
useCallback
,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新useMemo
,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算import React from 'react';
import ReactDOM from 'react-dom';
let Child = ({data,handleClick})=>{
console.log('Child render');
return (
<button onClick={handleClick}>{data.number}</button>
)
}
Child = React.memo(Child);
function App(){
console.log('App render');
const[name,setName]=React.useState('zhufeng');
const[number,setNumber]=React.useState(0);
let data = React.useMemo(()=>({number}),[number]);
let handleClick = React.useCallback(()=> setNumber(number+1),[number]);
return (
<div>
<input type="text" value={name} onChange={event=>setName(event.target.value)}/>
<Child data={data} handleClick={handleClick}/>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
src\react-dom.js
let hookStates = [];
let hookIndex = 0;
let scheduleUpdate;
function render(vdom,container){
mount(vdom,container);
scheduleUpdate = ()=>{
hookIndex = 0;
compareTwoVdom(container,vdom,vdom);
}
}
export function useState(initialState){
hookStates[hookIndex] = hookStates[hookIndex]||initialState;
let currentIndex = hookIndex;
function setState(newState){
if(typeof newState === 'function') newState=newState(hookStates[currentIndex]);
hookStates[currentIndex]=newState;
scheduleUpdate();
}
return [hookStates[hookIndex++],setState];
}
+export function useMemo(factory,deps){
+ if(hookStates[hookIndex]){
+ let [lastMemo,lastDeps] = hookStates[hookIndex];
+ let same = deps.every((item,index)=>item === lastDeps[index]);
+ if(same){
+ hookIndex++;
+ return lastMemo;
+ }else{
+ let newMemo = factory();
+ hookStates[hookIndex++]=[newMemo,deps];
+ return newMemo;
+ }
+ }else{
+ let newMemo = factory();
+ hookStates[hookIndex++]=[newMemo,deps];
+ return newMemo;
+ }
+}
+export function useCallback(callback,deps){
+ if(hookStates[hookIndex]){
+ let [lastCallback,lastDeps] = hookStates[hookIndex];
+ let same = deps.every((item,index)=>item === lastDeps[index]);
+ if(same){
+ hookIndex++;
+ return lastCallback;
+ }else{
+ hookStates[hookIndex++]=[callback,deps];
+ return callback;
+ }
+ }else{
+ hookStates[hookIndex++]=[callback,deps];
+ return callback;
+ }
+}
const ReactDOM = {
render
};
export default ReactDOM;
src\react.js
import {wrapToVdom} from './utils';
import {Component,PureComponent} from './Component';
+import {useState,useMemo,useCallback} from './react-dom';
const React = {
createElement,
Component,
PureComponent,
createRef,
createContext,
cloneElement,
memo,
+ useMemo,
+ useCallback,
useState
};
export default React;
src\index.js
import React from './react';
import ReactDOM from './react-dom';
function reducer(state={number:0}, action) {
switch (action.type) {
case 'ADD':
return {number: state.number + 1};
case 'MINUS':
return {number: state.number - 1};
default:
return state;
}
}
function Counter(){
const [state, dispatch] = React.useReducer(reducer,{number:0});
return (
<div>
Count: {state.number}
<button onClick={() => dispatch({type: 'ADD'})}>+</button>
<button onClick={() => dispatch({type: 'MINUS'})}>-</button>
</div>
)
}
ReactDOM.render(
<Counter/>,
document.getElementById('root')
);
src\react-dom.js
+export function useReducer(reducer, initialState) {
+ hookStates[hookIndex]=hookStates[hookIndex]||initialState;
+ let currentIndex = hookIndex;
+ function dispatch(action) {
+ hookStates[currentIndex]=reducer?reducer(hookStates[currentIndex],action):action;
+ scheduleUpdate();
+ }
+ return [hookStates[hookIndex++], dispatch];
+}
const ReactDOM = {
render
};
export default ReactDOM;
src\react.js
+import {useState,useMemo,useCallback,useReducer} from './react-dom';
const React = {
createElement,
Component,
PureComponent,
createRef,
createContext,
cloneElement,
memo,
useMemo,
useCallback,
useState,
+ useReducer
};
export default React;
static contextType = MyContext
或者 <MyContext.Consumer>
src\index.js
import React from './react';
import ReactDOM from './react-dom';
const CounterContext = React.createContext();
function reducer(state, action) {
switch (action.type) {
case 'add':
return {number: state.number + 1};
case 'minus':
return {number: state.number - 1};
default:
return state;
}
}
function Counter(){
let {state,dispatch} = React.useContext(CounterContext);
return (
<div>
<p>{state.number}</p>
<button onClick={() => dispatch({type: 'add'})}>+</button>
<button onClick={() => dispatch({type: 'minus'})}>-</button>
</div>
)
}
function App(){
const [state, dispatch] = React.useReducer(reducer, {number:0});
return (
<CounterContext.Provider value={{state,dispatch}}>
<Counter/>
</CounterContext.Provider>
)
}
ReactDOM.render(<App/>,document.getElementById('root'));
src\react.js
import {wrapToVdom} from './utils';
import {Component,PureComponent} from './Component';
import {useState,useMemo,useCallback,useReducer} from './react-dom';
function createElement(type, config, children) {
let ref;
if (config) {
delete config._owner;
delete config._store;
delete config.__self;
delete config.__source;
ref=config.ref;
delete config.ref;
}
let props = { ...config };
if (arguments.length > 3) {
props.children = Array.prototype.slice.call(arguments,2).map(wrapToVdom);
}else{
props.children = wrapToVdom(children);
}
return {
type,
ref,
props
};
}
function createRef() {
return { current: null };
}
function createContext(initialValue={}){
let context = {Provider,Consumer};
function Provider(props){
context._currentValue=context._currentValue||initialValue;
Object.assign(context._currentValue,props.value);
return props.children;
}
function Consumer(props){
return props.children(context._currentValue);
}
return context;
}
function cloneElement(element,newProps,...newChildren){
let oldChildren = element.props&&element.props.children;
let children = [...(Array.isArray(oldChildren)?oldChildren:[oldChildren]),...newChildren]
.filter(item=>item!==undefined)
.map(wrapToVdom);
if(children.length===1) children=children[0];
let props = {...element.props,...newProps,children};
return {...element,props};
}
function memo(OldComponent){
return class extends React.PureComponent{
render(){
return <OldComponent {...this.props}/>
}
}
}
+function useContext(context){
+ return context._currentValue;
+}
const React = {
createElement,
Component,
PureComponent,
createRef,
createContext,
cloneElement,
memo,
useMemo,
useCallback,
useState,
useReducer,
+ useContext
};
export default React;
componentDidMount
、componentDidUpdate
和 componentWillUnmount
具有相同的用途,只不过被合并成了一个 APIsrc\index.js
import React from './react';
import ReactDOM from './react-dom';
function Counter() {
const [number, setNumber] = React.useState(0);
React.useEffect(() => {
console.log('开启一个新的定时器')
const $timer = setInterval(() => {
setNumber(number => number + 1);
}, 1000);
return () => {
console.log('销毁老的定时器');
clearInterval($timer);
}
});
return (
<p>{number}</p>
)
}
ReactDOM.render(<Counter />, document.getElementById('root'));
src\react-dom.js
+export function useEffect(callback,dependencies){
+ if(hookStates[hookIndex]){
+ let [lastCallback,lastDeps] = hookStates[hookIndex];
+ let same = dependencies&&dependencies.every((item,index)=>item === lastDeps[index]);
+ if(same){
+ hookIndex++;
+ }else{
+ lastCallback&&lastCallback();
+ setTimeout(()=>{
+ lastCallback = callback();
+ hookStates[hookIndex++]=[lastCallback,dependencies];
+ });
+ }
+ }else{
+ setTimeout(()=>{
+ hookStates[hookIndex++]=[callback(),dependencies];
+ });
+ }
+}
const ReactDOM = {
render
};
export default ReactDOM;
src\react.js
import {wrapToVdom} from './utils';
import {Component,PureComponent} from './Component';
+import {useState,useMemo,useCallback,useReducer,useEffect} from './react-dom';
const React = {
createElement,
Component,
PureComponent,
createRef,
createContext,
cloneElement,
memo,
useMemo,
useCallback,
useState,
useReducer,
useContext,
+ useEffect
};
export default React;
useEffect
相同,但它会在所有的 DOM
变更之后同步调用 effectuseEffect
不会阻塞浏览器渲染,而 useLayoutEffect
会浏览器渲染useEffect
会在浏览器渲染结束后执行,useLayoutEffect
则是在 DOM
更新完成后,浏览器绘制之前执行src\index.js
import React from './react';
import ReactDOM from './react-dom';
const Animate = ()=>{
const ref = React.useRef();
React.useLayoutEffect(()=>{
ref.current.style.WebkitTransform = `translate(500px)`;
ref.current.style.transition = `all 500ms`;
});
let style = {
width:'100px',
height:'100px',
backgroundColor:'red'
}
return (
<div style={style} ref={ref}>我是内容</div>
)
}
ReactDOM.render(<Animate/>,document.getElementById('root'));
src\react-dom.js
+export function useLayoutEffect(callback,dependencies){
+ if(hookStates[hookIndex]){
+ let [lastCallback,lastDeps] = hookStates[hookIndex];
+ let same = dependencies&&dependencies.every((item,index)=>item === lastDeps[index]);
+ if(same){
+ hookIndex++;
+ }else{
+ lastCallback&&lastCallback();
+ queueMicrotask(()=>{
+ lastCallback = callback();
+ hookStates[hookIndex++]=[lastCallback,dependencies];
+ });
+ }
+ }else{
+ queueMicrotask(()=>{
+ hookStates[hookIndex++]=[callback(),dependencies];
+ });
+ }
+}
+export function useRef(initialState) {
+ hookStates[hookIndex] = hookStates[hookIndex] || { current: initialState };
+ return hookStates[hookIndex++];
+}
src\react.js
import {wrapToVdom} from './utils';
import {Component,PureComponent} from './Component';
+import {useState,useMemo,useCallback,useReducer,useEffect,useRef,useLayoutEffect} from './react-dom';
const React = {
createElement,
Component,
PureComponent,
createRef,
createContext,
cloneElement,
memo,
useMemo,
useCallback,
useState,
useReducer,
useContext,
useEffect,
useRef,
+ useLayoutEffect
};
export default React;
useImperativeHandle
可以让你在使用 ref 时自定义暴露给父组件的实例值import React from './react';
import ReactDOM from './react-dom';
function Child(props, ref) {
const inputRef = React.useRef();
React.useImperativeHandle(ref, () => (
{
focus() {
inputRef.current.focus();
}
}
));
return (
<input type="text" ref={inputRef} />
)
}
const ForwardChild = React.forwardRef(Child);
function Parent() {
let [number, setNumber] = React.useState(0);
const inputRef = React.useRef();
function getFocus() {
console.log(inputRef.current);
inputRef.current.value = 'focus';
inputRef.current.focus();
}
return (
<div>
<ForwardChild ref={inputRef} />
<button onClick={getFocus}>获得焦点</button>
<p>{number}</p>
<button onClick={() => {
debugger
setNumber( number + 1)
}}>+</button>
</div>
)
}
ReactDOM.render(<Parent/>,document.getElementById('root'));
src\react-dom.js
function mountClassComponent(vdom){
+ const {type, props,ref} = vdom;
const classInstance = new type(props);
+ if(ref){
+ ref.current = classInstance;
+ classInstance.ref = ref;
+ }
vdom.classInstance=classInstance;
if(type.contextType){
classInstance.context = type.contextType.Provider._value;
}
if(classInstance.componentWillMount)
classInstance.componentWillMount();
classInstance.state = getDerivedStateFromProps(classInstance,classInstance.props,classInstance.state)
const renderVdom = classInstance.render();
classInstance.oldRenderVdom=vdom.oldRenderVdom=renderVdom;
const dom = createDOM(renderVdom);
if(classInstance.componentDidMount)
dom.componentDidMount=classInstance.componentDidMount.bind(classInstance);
return dom;
}
+export function useImperativeHandle(ref,handler){
+ ref.current = handler();
+}
const ReactDOM = {
render
};
export default ReactDOM;
src\react.js
import {wrapToVdom} from './utils';
import {Component,PureComponent} from './Component';
+import {useState,useMemo,useCallback,useReducer,useEffect,useRef,useLayoutEffect,useImperativeHandle} from './react-dom';
+function forwardRef(FunctionComponent){
+ return class extends Component{
+ render(){
+ return FunctionComponent(this.props,this.ref);
+ }
+ }
+}
const React = {
createElement,
Component,
PureComponent,
createRef,
createContext,
cloneElement,
memo,
useMemo,
useCallback,
useState,
useReducer,
useContext,
useEffect,
useRef,
useLayoutEffect,
+ forwardRef,
+ useImperativeHandle
};
export default React;