React的记录

栏目: IOS · Android · 发布时间: 6年前

内容简介:New PropssetState()forceUpdate()
React的记录

生命周期详解

constructor 实例化

class A extends React.Component{
	constructor(){
		super();
	}
}
复制代码
  • 实例化对象
  • 继承React.Component
  • 初始化state

static getDerivedStateFromProps

class A extends React.Component{
	static getDerivedStateFromProps(props,state){
		return Object
	}
}
复制代码
  • update更新时,render之前,dom渲染前
  • 必须有初始化state
  • 静态方法,没有this
  • 可以return出对象,设置state
class Count extends Component{
  constructor(props){
    super(props);
    this.state = {
      count: 0,
    }
  };

  static getDerivedStateFromProps(props){
    return {count: props.count*2};
  };

  render(){
    return (
      <React.Fragment>
        <p>Count-Child-state: {this.state.count}</p>
        <p>Count-Child-props: {this.props.count}</p>
      </React.Fragment>
    )
  }
};

class Time extends Component{
  constructor(props){
    super(props);
    this.state={
      time: this.props.time,
    }
  };

  static getDerivedStateFromProps(props,state){
    console.log(state);
    return null;
  };

  handleClick(){
    this.setState({
      time: new Date().toString()
    });
  }

  render(){
    const {time} = this.state;
    return(
      <React.Fragment>
        <input type='button' value='setState触发 详情看log' onClick={()=>this.handleClick()}/> 
        <p>{time}</p>
      </React.Fragment>
    )
  };
};

function GetDerivedStateFromPropsComponent (){
  const [count,setCount] = useState(0);
  const [time] = useState(new Date().toString());

  return (
    <Fieldset title='getDerivedStateFromProps'>
      <input type='button' value='new props ++' onClick={()=>setCount(s=>s+1)}/>
      <Count count={count}/>
      <Time time={time}/>
    </Fieldset>
  )
};
复制代码

shouldComponentUpdate

class A extends React.Component{
	shouldComponentUpdate(nextProps, nextState){
		return Boolean
	}
}
复制代码
  • update更新时,render之前,dom渲染前
  • return 一个布尔值来判断是否渲染
  • 默认return true
class Time extends Component{
  shouldComponentUpdate(nextPorps){
    if(this.props.time !== nextPorps.time){
      // 保证每次time不同时更新
      console.group('shouldComponent-props');
      console.log(nextPorps);
      console.log(this.props);
      console.groupEnd();
      return true;
    }
    return false;
  };
  
  render(){
    return(
      <React.Fragment>
        {this.props.time}
      </React.Fragment>
    )
  }
};

function ShuldComponentUpdateComponent() {
  const [time,setTime] = useState(new Date().toString());

  return(
    <Fieldset title='shuldComponentUpdate'>
      <input type='button' value='new Props' onClick={()=>setTime(new Date().toString())} />
      <Time time={time}/>
    </Fieldset>
  )
};
复制代码

render

class A extends React.Component{
	render(){
		return JSX
	}
}
复制代码
  • 将jsx渲染为真实DOM

getShapshotBeforeUpdate

class A extends React.Component{
	getShapshotBeforeUpdate(prevProps,prevState){
		return ‘getShapshotBeforeUpdate’
	}
	componentDidUpdate(prevProps,prevState,snapshot){
	   // TODO snapshot == ‘getShapshotBeforeUpdate’
	}
}
复制代码
  • update更新时,render之前,dom渲染前
  • 无默认返回值,可返回null
  • return 一个值,用来作为componentDidUpdate的第三个参数
class Child extends Component {
  constructor(props) {
    super(props);
    this.state = {
      ...props,
    };
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.group('getSnapshotBeforeUpdate');
    console.log(prevProps);
    console.log(prevState);
    console.groupEnd();
    if (prevProps.time !== prevState.time) {
      this.setState({
        time: prevProps.time,
      });
    }
    return 'getSnapshotBeforeUpdate';
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.group('componentDidUpdate');
    console.log(prevProps);
    console.log(prevState);
    console.log(snapshot);
    console.groupEnd();
  }

  render() {
    return (
      <p>
        {this.state.time}
      </p>
    );
  }
}

function GetShapshotBeforeUpdateComponent() {
  const [time, setTime] = useState(new Date().toString());

  return (
    <Fieldset title='getShapshotBeforeUpdate'>
      <Child time={time} />
      <input type='button' value='更新时间' onClick={()=>setTime(new Date().toString())} />
    </Fieldset>
  );
}
复制代码

componentDidMount

class A extends React.Component{
	componentDidMount(){}
}
复制代码
  • render之后,dom渲染后
  • 唯一的会在服务端渲染调起的生命周期钩子函数。
class Time extends Component {
  componentDidMount() {
    console.log('ComponentDidMount');
  }

  render() {
    return (
      <p>{new Date().toString()}</p>
    );
  }
}

function ComponentDidMountComponent() {
  const [show, setShow] = useState(false);
  return (
    <Fieldset title='componentDidMount'>
      <input type='button' value='触发componentDidMount' onClick={()=>setShow(!show)} />
      { show && <Time />}
    </Fieldset>
  );
}
复制代码

componentDidUpdate

class A extends React.Component{
	componentDidUpdate(prevProps,prevState,snapshot){}
}
复制代码
  • update更新时,render之后,dom渲染后
  • snapshot为getShapshotBeforeUpdate的return
参照getShapshotBeforeUpdate的code demo
复制代码

componentWillUnmount

class A extends React.Component{
	componentWillUnmount(){}
}
复制代码
  • dom卸载
class Time extends Component {
  componentWillUnmount() {
    console.log('componentWillUnmount');
  }
  render() {
    return (
      <p>{new Date().toString()}</p>
    );
  };
}

function ComponentWillUnmountComponent() {
  const [show, setShow] = useState(true);
  return (
    <Fieldset title='componentWillUnmount'>
      <input type='button' value='触发componentWillUnmount' onClick={()=> setShow(!show)} />
      {show && <Time />}
    </Fieldset>
  );
}
复制代码

componentDidCatch

class A extends React.Component{
    componentDidCatch(error,errorInfo){}
}
复制代码
  • 只有在render中报错才会被捕获
  • 为什么不用try/catch:try/catch更加适合命令式代码,而componentDidCatch会捕获组件树中的报错
class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state={
      error: false,
      errorInfo: null,
    };
  }

  componentDidCatch(error, errorInfo) {
    console.log(errorInfo.componentStack);
    this.setState({
      error: error.toString(),
      errorInfo: errorInfo.componentStack,
    });
  }

  render() {
    if (this.state.error) {
      return (
        <div>
          <h1>error: {this.state.error}</h1>
          <p>
            errorInfo:{this.state.errorInfo}
          </p>
        </div>
      );
    }
    return this.props.children;
  }
};
ErrorBoundary.propTypes = {
  children: PropTypes.node.isRequired,
};

class Input extends Component {
  constructor() {
    super();
    this.state= {
      throw: false,
    };
  }

  handleClick() {
    this.setState((s)=>({
      throw: !s.throws,
    }));
  }

  render() {
    if (this.state.throw) throw new Error('I throw');
    return <input type='button' value='触发catch' onClick={()=> this.handleClick()} />;
  }
};

function ComponentDidCatchComponent() {
  return (
    <Fieldset title='componentDidCatch'>
      <ErrorBoundary>
        <Input />
      </ErrorBoundary>
    </Fieldset>
  );
};
复制代码

触发流程

初始化

constructor()
static getDerivedStateFromProps(props,state)
render()
componentDidMount()
复制代码
class Flow extends React.Component{
  constructor(){
    super();
    this.state = {};
    console.group('初始化阶段');
    console.log('constructor');
  };

  static getDerivedStateFromProps(){
    console.log('getDerivedStateFromProps');
    return null;
  };

  componentDidMount(){
    console.log('componentDidMount');
    console.groupEnd();
  }
  
  render(){
    console.log('render');
    return (
      <p>
        constructor<br/>
        |<br/>
        static getDerivedStateFromProps()<br/>
        |<br/>
        render<br/>
        |<br/>
        componentDidMount<br/>
      </p>
    )
  };
}


function MountingComponent (){
  const [show,setShow] = useState(false);

  return (
    <Fieldset title='初始化阶段'>
      <input type='button' value='查看初始化阶段,详情看log' onClick={()=>setShow(!show)}/>
      {show && <Flow/>}
    </Fieldset>
  )
};
复制代码

属性更新

New Props

static getDerivedStateFromProps(props,state)
shouldComponentUpdate(nextProps, nextState)
render()
getSnapshotBeforeUpdate(prevProps,prevState)
componentDidUpdate()
复制代码
class Flow extends React.Component{
  state = {};

  static getDerivedStateFromProps(){
    console.group('触发传递新的props');
    console.log('getDevivedStateFromProps');
    return null;
  };

  shouldComponentUpdate(){
    console.log('shouldComponentUpdate');
    return true;
  };

  getSnapshotBeforeUpdate(){
    console.log('getSnapshotBeforeUpdate');
    return null;
  };

  componentDidUpdate(){
    console.log('componentDidUpdate');
    console.groupEnd();
  };

  render(){
    console.log('render');
    return(
      <React.Fragment>
        <p>{this.props.title}</p>
        <p>
          static getDevivedStateFromPorps <br/>
          | <br/>
          shouldComponentUpdate <br/>
          | <br/>
          getSnapshotBeforeUpdate <br/>
          | <br/>
          componentDidUpdate
        </p>
      </React.Fragment>
    )
  }
};

function NewpropsComponent(){
  const [time,setTime] = useState(new Date().toString());

  return (
    <Fieldset title='传递新的props'>
      <input type='button' value='触发传递新的props' onClick={()=>setTime(new Date().toString())}/>
      <Flow title={time}/>
    </Fieldset>
  )
};

export default NewpropsComponent;
复制代码

setState()

static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
复制代码
class SetStateComponent extends React.Component{
  state = {
    time: new Date().toString(),
  };
  
  static getDerivedStateFromProps(){
    console.group('设置属性');
    console.log('getDerivedStateFromProps');
    return null;
  };

  shouldComponentUpdate(){
    console.log('shouldComponentUpdate')
    return true;
  };

  getSnapshotBeforeUpdate(){
    console.log('getSnapshotBeforeUpdate');
    return null;
  };

  componentDidUpdate(){
    console.log('componentDidUpdate');
    console.groupEnd();
  };

  handleClick(){
    this.setState({
      time: new Date().toString()
    });
  }

  render(){
    console.log('render');
    return (
      <Fieldset title='设置属性'>
        <input type='button' value='更新时间' onClick={()=>this.handleClick()} />
        <p>{this.state.time}</p>
        static getDerivedStateFromProps <br />
        | <br />
        shouldComponentUpdate <br />
        | <br />
        render <br />
        | <br />
        getSnapshotBeforeUpdate <br />
        | <br />
        componentDidUpdate <br />
      </Fieldset>
    )
  }
}
复制代码

forceUpdate()

getDerivedStateFromProps()
render()
getShapshotBeforeUpdate()
componentDidUpdate()
复制代码
class ForceUpdateComponent extends React.Component{
  state = {};

  static getDerivedStateFromProps(){
    console.group('forecUpdate更新');
    console.log('getDerivedStateFromProps');
    return true;
  };

  getSnapshotBeforeUpdate(){
    console.log('getShapshotBeforeUpdate');
    return null;
  };

  componentDidUpdate(){
    console.log('componentDidUpdate');
    console.groupEnd();
  }

  render(){
    console.log('render')
    return(
      <Fieldset title='forecUpdate更新'>
        <input type='button' value='触发forecUpdae' onClick={()=>this.forceUpdate()} /> <br/>
        static getDerivedStateFromProps <br/>
        | <br/>
        getShapshotBeforeUpdate <br/>
        | <br/>
        componentDidUpdate <br/>
      </Fieldset>
    )
  }
}
复制代码

触发生命周期的api

setState 设置状态

setState((State)=>({object}),function callback])
复制代码
// 完全写法
this.setState(State=>({
    ...State
}),()=>{
    ...
})

// 简单写法
this.setState({
    ...
})
复制代码
getDerivedStateFromProps->shouldComponentUpdate->render->getSnapshotBeforeUpdate->componentDidUpdate

forceUpage 强制更新

forceUpage(function callback)
复制代码
this.forceUpage(()=>{
    ...
})
复制代码
getDerivedStateFromProps->render->componentDidUpdate

React.Component

  • react的抽象基础类
class A extends React.Component{}
复制代码

React.PureComponent

  • 内置一个浅比较,比较props和state,决定是否render,用于提高性能
  • 只是进行浅对比,无法对比复合数据
  • 用于避免无状态组件的重复渲染
class A extends React.PureComponent{}
复制代码

React.memo(()=>{},()=>{});

  • 组件仅在它的 props 发生改变的时候进行重新渲染
  • 和React.PureComponent类似功能一直,但这是一个纯函数用法
  • 只是进行浅对比,无法对比复合数据
const A = React.memo((props)=>{
    return JSX
},(prevProps,nextProps)=>{
    return null | false
})
复制代码
const MemoComponent = React.memo((props)=>{
  return (
    <div>{props.time}</div>
  );
}, (prevProps, nextProps)=>{
  if (prevProps.time !== nextProps.time) {
    return null;
  }
  return false;
});

function Memo() {
  const [time, setTime] = useState(new Date().toString());

  return (
    <Fieldset title='memo'>
      memo
      <input type='button' value='更新时间' onClick={()=>setTime(new Date().toString())} />
      <MemoComponent time={time} />
    </Fieldset>
  );
};
复制代码

React.createRef()

  • 创建节点引用
  • 通过ref属性附加到React元素
  • 可以通过curent访问到节点
  • ref更新发生在componentDidMount、componentDidUpdate时
  • 无法在函数组件上使用,因为没有实例
class CreateRefComponent extends Component {
  constructor() {
    super();
    this.state = {
      el: null,
    };
    this.ref = React.createRef();
  }

  componentDidMount() {
    this.setState({
      el: this.ref.current.innerHTML,
    });
  }

  render() {
    return (
      <Fieldset title='createRef'>
        <div ref={this.ref}>createRef</div>
        <p>el: {this.state.el}</p>
      </Fieldset>
    );
  }
}
复制代码

React.isValidElement(object)

  • 验证对象是否为React元素。返回true或false。
  • 必须传递一个对象
let D = React.createElement('div');
React.isValidElement(D);
复制代码
function ChildFunction() {
  return (
    <div>Child-function</div>
  );
};

class ChildComponent extends Component {
  render() {
    return (
      <div>Child-Component</div>
    );
  }
};

function IsValidElementComponent() {
  const D = React.createElement('div');

  return (
    <Fieldset title='isValidElement'>
      <p>createElement: {React.isValidElement(D).toString()}</p>
      <p>Function Component: {React.isValidElement(ChildFunction()).toString()}</p>
      <p>class Component: {React.isValidElement(<ChildComponent />).toString()}</p>
    </Fieldset>
  );
};
复制代码

React.Children

  • 遍历children,返回数组
array React.Children.map(children,function callback) 
复制代码
class Map extends Component {
  render() {
    const arr = React.Children.map(this.props.children, (v)=> v);

    return (
      <Fieldset title='map'>
        {arr}
      </Fieldset>
    );
  }
};
Map.propTypes = {
  children: PropTypes.node.isRequired,
};

function Index() {
  return (
    <Map>
      <p>A</p>
      <p>B</p>
      <p>C</p>
      <p>D</p>
      <p>E</p>
      <p>F</p>
    </Map>
  );
};
复制代码
  • 遍历children,无返回
void React.Children.forEach(children,function callback) 
复制代码
class ForEact extends Component {
  render() {
    const arr = [];
    React.Children.forEach(this.props.children, (v) => arr.push(v));
    return (
      <Fieldset title='ForEact'>
        {arr}
      </Fieldset>
    );
  }
};
ForEact.propTypes = {
  children: PropTypes.node.isRequired,
};

function Index() {
  return (
    <ForEact>
      <p>A</p>
      <p>B</p>
      <p>C</p>
      <p>D</p>
      <p>E</p>
      <p>F</p>
    </ForEact>
  );
};
复制代码
  • 返回children的长度
number React.Children.count(children) 
复制代码
class Count extends Component {
  render() {
    const count = React.Children.count(this.props.children, (v)=> v);

    return (
      <Fieldset title='count'>
        {count}
      </Fieldset>
    );
  }
};
Count.propTypes = {
  children: PropTypes.node.isRequired,
};

function Index() {
  return (
    <Count>
      <p>A</p>
      <p>B</p>
      <p>C</p>
      <p>D</p>
      <p>E</p>
      <p>F</p>
    </Count>
  );
};
复制代码
  • 判断children是否为1,不为1报错
void React.Children.only(children) 
复制代码
class Only extends Component {
  render() {
    const {title} = this.props;

    let e = null;
    let o = null;

    try {
      o = React.Children.only(this.props.children);
    } catch (error) {
      e = error;
    };
    return (
      <Fieldset title={title}>
        {e && Object.keys(e).map((v) => e[v])}
        {o}
      </Fieldset>
    );
  }
};
Only.propTypes = {
  children: PropTypes.node.isRequired,
  title: PropTypes.string.isRequired,
};

function Index() {
  return (
    <React.Fragment>
      <Only title='not-Only'>
        <p>A</p>
        <p>B</p>
        <p>C</p>
        <p>D</p>
        <p>E</p>
        <p>F</p>
      </Only>
      <Only title='Only'>
        <p>A</p>
      </Only>
    </React.Fragment>
  );
};
复制代码
  • 把children,从对象转换为数组
array React.Children.toArray(children,function callback)
复制代码
class ToArray extends Component {
  render() {
    const arr = React.Children.toArray(this.props.children);

    return (
      <Fieldset title='toArray'>
        <p>isArray: {Array.isArray(arr).toString()}</p>
        {arr}
      </Fieldset>
    );
  }
};
ToArray.propTypes = {
  children: PropTypes.node.isRequired,
};

function Index() {
  return (
    <ToArray>
      <p>A</p>
      <p>B</p>
      <p>C</p>
      <p>D</p>
      <p>E</p>
      <p>F</p>
    </ToArray>
  );
};
复制代码

React.Fragment

  • 允许创建一对React.Fragment的元素,这样就不用额外创建用于包裹的元素
// 完全写法
render(){
    return(
        <React.Fragment>
            hello React
        </React.Fragment>
    )
}

// 缩略写法
render(){
    return(
        <>
            hello React
        </>
    )
}
复制代码

React.createElement

  • 创建react对象
React.createElement(
  type,
  [props],
  [...children]
);
复制代码
class Hello extends React.Component {
  render() {
    return React.createElement('div', null, `Hello ${this.props.toWhat}`);
  }
}

ReactDOM.render(
  React.createElement(Hello, {toWhat: 'World'}, null),
  document.getElementById('root')
);
复制代码

React.createFactory

  • 返回一个React元素的函数。
React.createFactory({
  type,
  [props],
  [...children]
})
复制代码
function createFactory() {
  const li = React.createFactory('li');
  const Child1 = li(null, 'child-1');
  const Child2 = li(null, 'child-2');

  const Root = React.createElement('ul', '', Child1, Child2);

  return (
    <Fieldset title='createFactory'>
      {Root}
    </Fieldset>
  );
}
复制代码

React.cloneElement

  • 克隆并返回一个新的React元素
React.cloneElement({
    type,
    [props],
    [...children]
})
复制代码
class C extends React.Component {
  render(){
    var D = React.createElement('div');
    return React.cloneElement(
      D,
      null,
      'hello React'
    );
  }
}
复制代码

React.forwordRef(props,ref)

TODO @jingwen 需要独立文章分析

  • 引用转发ref给组件
React.forwordRef(props,ref)
复制代码
// 简单用法
function Input(props) {
  const {forwardRef} = props;
  return (
    <input type='input' defaultValue='输入框' ref={forwardRef} />
  );
};
Input.propTypes = {
  forwardRef: propTypes.object,
};
Input.defaultProps={
  forwardRef: {},
};

class ForwardRefComponent extends Component {
  constructor() {
    super();
    this.state = {
      refs: React.createRef(),
    };
  }

  handleFocus() {
    const {refs: {current}} = this.state;
    current.focus();
  }

  render() {
    const {refs} = this.state;
    const FancyInput = React.forwardRef((props, ref)=>{
      return <Input forwardRef={ref} />;
    });

    return (
      <Fieldset title='forwardRef-简单用法'>
        <FancyInput ref={refs} />
        <input type='button' value='获取焦点' onClick={()=>this.handleFocus()} />
      </Fieldset>
    );
  }
}
复制代码
// 转发ref给后代
function wrapped(Component) {
  class Wrapped extends React.Component {
    render() {
      const {forwardedRef, ...props} = this.props;
      return <Component ref={forwardedRef} {...props} />;
    }
  }
  return React.forwardRef((props, ref) => {
    return <Wrapped {...props} forwardedRef={ref} />;
  });
}

class Input extends React.Component {
  constructor(props) {
    super(props);
    this.ref = React.createRef();
  };

  handleFocus() {
    const {current} = this.ref;
    current.focus();
  }

  render() {
    const props = this.props;
    return (
      <input type='input' defaultValue={props.value} ref={this.ref} />
    );
  }
}

class Hoc extends React.Component {
  constructor() {
    super();
    this.state = {
      refs: React.createRef(),
    };
  };

  handleFocus() {
    const {refs: {current}} = this.state;
    current.handleFocus();
  }

  render() {
    const {refs} = this.state;
    const InputHoc = wrapped(Input);

    return (
      <Fieldset title='forwardRef-hoc-用法'>
        <InputHoc value='forwardRef-hoc-用法' ref={refs} />
        <input type='button' value='获取焦点' onClick={()=>this.handleFocus()} />
      </Fieldset>
    );
  }
};
复制代码

React.lazy or React.Suspense

  • React.lazy,动态导入组件
  • React.Suspense,等待导入时的显示
const TimeFun = lazy(()=> import('./TimeFun'));
const TimeClass = lazy(()=> import('./TimeClass'));

class Index extends Component {
  render() {
    const Time = new Date().toString();
    return (
      <Fieldset title='lazy'>
      <Suspense fallback={<div>Loading...</div>}>
        <p>parent: {Time}</p>
        <TimeFun />
        <TimeClass />
      </Suspense>
      </Fieldset>
    );
  }
}
复制代码
class TimeClass extends React.Component {
  render() {
    const Time = new Date().toString();
    return <div>lazy-time-class: {Time}</div>;
  };
};

export default TimeClass;
复制代码
function TimeFun() {
  const Time = new Date().toString();
  return (<div>lazy-time-fun: {Time}</div>);
};

export default TimeFun;
复制代码

React.StrictMode

  • 给组件的任何部分开启严格模式
  • 识别具有不安全生命周期的组件
  • 关于遗留字符串ref API使用的警告
  • 关于已弃用的findDOMNode用法的警告
  • 检测意外的副作用
  • 检测遗留上下文API
class Index extends Component {
  render() {
    return (
      <Fieldset title='StrictMode'>
        <StrictMode>
          StrictMode
        </StrictMode>
      </Fieldset>
    );
  }
};
复制代码

Context

  • 组件设置全局参数
  • createContext,设置context的默认值
const A = React.createContext('aaaa');
复制代码
  • Context.Provider,允许组件订阅该context的参数
  • Class.contextType,设置当前组件订阅的context。当找不到Provider的时候使用。context值可以在componentDidMount、componentUpdate、componentWillUnmount、render中获取
// 两种设置形式
const context = React.createContext('aaa');
class A extend React.Component{
    static contextType = context;
}

class B extend React.Component{

}
B.contextType = context;
复制代码
  • Context.Consumer,
// 简单例子
const ThemeContext = React.createContext('light');

class ThemedButton extends React.Component {
  static contextType = ThemeContext;
  render() {
    return <input defaultValue={this.context} type='input' />;
  }
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class Index extends Component {
  render() {
    return (
      <Fieldset title='context'>
        <Toolbar />
        <ThemeContext.Provider value="dark">
          <Toolbar />
        </ThemeContext.Provider>
      </Fieldset>
    );
  }
}
复制代码
  • Consumer,设置多个上下文
const themes = {
  light: {
    background: '#eeeeee',
  },
  dark: {
    background: '#222222',
  },
};

const ThemeContext = createContext(
  themes.light,
);

class ThemeButton extends Component {
  static contextType = ThemeContext;
  render() {
    const props = this.props;
    const theme = this.context;
    return (
      <button type='button' {...props} style={{backgroundColor: theme.background}} >
        Theme
      </button>
    );
  }
};

class Index extends Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };
  }

  handleTheme() {
    this.setState((state)=>({
      theme: JSON.stringify(state.theme) === JSON.stringify(themes.dark)
        ? themes.light
        : themes.dark,
    }));
  };

  render() {
    return (
      <Fieldset title='多context'>
        <ThemeContext.Provider value={this.state.theme}>
          <ThemeButton onClick={()=>this.handleTheme()}>
            Change Theme
          </ThemeButton>
        </ThemeContext.Provider>
        <ThemeButton />
      </Fieldset>
    );
  }
}
复制代码

ReactDOM.createPortal

ReactDOM.createPortal(
    React.Component | JSX, // react组件
    element,// 节点元素
);
复制代码

defaultProps

  • 设置props的默认值
class A extends React.Component{
    constructor(props){
        super(props);
        console.log(props)
    };
    
    // 集成写法
    static defaultProps = {
        test: 'test',        
    };
}

// 外置写法
A.defaultPorps = {
    test: 'test',
}
复制代码

PropTypes

TODO
复制代码

Render Props

  • 解决组件交叉使用问题
function Cat(props) {
  const STYLE = {
    width: 20,
    height: 20,
    background: '#FF00FF',
    position: 'absolute',
    left: props.mouse.x,
    top: props.mouse.y,
  };

  return (
    <div style={STYLE} />
  );
}
Cat.propTypes = {
  mouse: propTypes.object.isRequired,
};

class Mouse extends Component {
  constructor(props) {
    super(props);
    this.state={
      x: 0,
      y: 0,
    };
  }

  handleMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY,
    });
  };

  render() {
    const STYLE = {
      width: 500,
      height: 500,
      background: '#eee',
      display: 'inline-block',
    };

    return (
      <div style={STYLE} onMouseMove={(e)=>this.handleMove(e)}>
        {this.props.render(this.state)}
      </div>
    );
  }
}
Mouse.propTypes = {
  render: propTypes.func.isRequired,
};

class Index extends Component {
  render() {
    return (
      <Fieldset title='render props'>
        <Mouse render={(mouse)=>(
          <Cat mouse={mouse} />
        )} />
      </Fieldset>
    );
  }
};
复制代码

React的事件池

TODO
复制代码

Hooks

useState 状态钩

  • 用来设置和更新状态
const [状态变量,更新函数] = useState(默认值)
复制代码
function A(){
  const [count, setCount] = useState(0);

  return(
    <React.Fragment>
      <p>Count: {count}</p>
      <button onClick={()=>{setCount(count+1)}}>++</button>
      <button onClick={()=>{setCount(count-1)}}>--</button>
    </React.Fragment>
  )
};
复制代码

useEffect(function,[]) 效果钩

useEffect(()=>{
    // componentDidMount
    // componentDidUpdate
    return ()=>{
        // componentWillUnmount
    }
},[]);
复制代码
  • useEffect是componentDidMount、componentDidUpdate、componentWillUnmount的结合。
  • 默认情况下,useEffect,会在每次componentDidMount、componentDidUpdate时执行。
  • 可以拿到DOM结构
function A(){
  const [count,setCount] = useState(0)

  useEffect(()=>{
    document.getElementById('useEffect-A-count').innerText = `count: ${count}`;
  });

  function handleClick(){
    setCount(count+1);
  };

  return (
    <Fieldset title='useEffect-简单状态'>
      <p id='useEffect-A-count'></p>
      <input type='button' value='更新' onClick={handleClick}/>
    </Fieldset>
  )
};
复制代码
  • 如何在组件compoenntWillUnmount时触发useEffect
function B(){
  const [show,setShow] = useState(true);
  function Child(){
    useEffect(()=>{
      console.log('componentDidMount');
      return ()=>{
        console.log('componentWillUnmount');
      }
    })
    return <p>B-Child</p>
  };

  return (
    <Fieldset title='useEffect-在componentWillUnmunt时触发'>
      <p>请查看log</p>
      <input type='button' value='show' onClick={()=>setShow(!show)}/>
      {show && <Child/>}
    </Fieldset>
  )
};
复制代码
  • 利用第二个参数,让useEffect不会过度渲染
  • 为空数组,表示不依赖props或者state,将不会更新
  • 数组内表示当新的赋值,如果新值和旧值不想等,就更新
function D(){
  const [count,setCount] = useState(0);
  const [double,setDouble] = useState(count*2);
  const [three,setThree] = useState(count*3);
  
  useEffect(()=>{
    setDouble(count*2);
  },[count*2])

  useEffect(()=>{
    setThree(count);
  },[])// 为空数组,表示不依赖props或者state,将不会更新

  return (
    <Fieldset title='userEffect-利用第二个参数跳过效果优化'>
      <p>+1: {count}</p>
      <p>*2: {double}</p>
      <p>not: {three}</p>
      <input type='button' value='click' onClick={()=>setCount(count+1)}/>
    </Fieldset>
  )
};
复制代码

useLayoutEffect(function,[])

  • DOM渲染前反应的钩子
  • 基本配置和useEffect一致
function E(){
  const [name,setName] = useState(null);
  useLayoutEffect(()=>{
    setTimeout(()=>{
      setName('useLayoutEffect');
    },3000);
  },[]);
  useEffect(()=>{
    setTimeout(()=>{
      setName('useEffect');
    },3000);
  },[]);

  return(
    <Fieldset title='useLayoutEffecth和useEffect的区别'>
      <span id='e-useEffect'>{name}</span>
    </Fieldset>
  )
}
复制代码

useContext(createContext)

  • 参数在所有子组件中都能获取
const CountContext = createContext();
function F(){
  function ChildA(){
    const countChild = useContext(CountContext);
    return (
      <p>
        Child A count: {countChild}
      </p>
    )
  };

  function ChildB(){
    const countChild = useContext(CountContext);
    return (
      <p>
        Child B count: {countChild}
      </p>
    )
  };

  const [count,setCount] = useState(0);

  return (
    <Fieldset title='useContext'>
      <CountContext.Provider value={count}>
        <ChildA />
        <ChildB />
      </CountContext.Provider>
      <input type='button' value='++' onClick={()=>setCount(count+1)}/>
    </Fieldset>
  )
}
复制代码

useReducer(reducer,initialState)

  • 类似redux的功能
const [state,dispatch] = useReducer(reducer,initialState,{action});
// const [属性,动作] = useReducer(动作函数,初始值属性,初始动作);
// 如何触发:dispatch(type);
复制代码
function reducer(state,action){
  switch(action){
    case 'reset':
      return 0;
      break;
    case 'increment':
      return state+1;
      break;
    case 'decrement':
      return state-1;
      break;
    default:
      return state;
  };
};

function useReduceComponent(){
  const [state,dispatch] = useReducer(reducer,0,'reset');

  return (
    <Fieldset title='useReduce'>
      <p>{state}</p>
      <input type='button' value='++' onClick={()=>dispatch('increment')}/>
      <input type='button' value='--' onClick={()=>dispatch('decrement')}/>
      <input type='button' value='reset' onClick={()=>dispatch('reset')}/>
    </Fieldset>
  );
};
复制代码

useCallback(()=>{},[])

  • 返回一个记忆的memoized,默认情况下会记录上一次传递的值
  • 可以替代shouldComponentUpdate使用
function MemoizedConst ({num}){
  const memoizedCallback = useCallback(()=>{
    return num;
  },[]);

  return (
    <Fieldset title='MemoizedConst'>
      <p>记忆 num > {memoizedCallback()}</p>
      <p>原始 num > {num}</p>
    </Fieldset>
  )
};

function UseCallbackComponent (){
  let [num,setNum] = useState([1,2,3]);
  useEffect(()=>{
    setTimeout(function(){
      setNum([3,4,5])
    },3000);
  },[]);
  
  return (
    <Fieldset title='useCallback'>
      <MemoizedConst num={num}/>
    </Fieldset>
  );
};

// [1,2,3]
// dely 3000ms
// [3,4,5]
复制代码
  • 保存事件,让组件不会重复创建新方法,当你重复点击时,InputComponentState的handleClick的事件总是由组件新创建的。而InputComponentCallback的handleClick会被记忆下来,不会新创建。有利于提高性能。
let InputComponentStateFunc = null;
function InputComponentState(){
  const [state,setState] = useState(0);
  function handleClick(){
    setState(state=> state+1);
  };
  
  console.group('InputComponentStateFunc');
    console.log(InputComponentStateFunc === handleClick);
  console.groupEnd();

  InputComponentStateFunc = handleClick;

  return (
    <Fieldset title='InputComponentState'>
      <input type='button' value={state} onClick={handleClick}/>
    </Fieldset>
  )
};

let InputComponentCallbackFunc = null;
function InputComponentCallback(){
  const [state,setState] = useState(0);
  const handleClick = useCallback((event) => {
    setState(state=> state+1);
    event.persist();
  },[]);
  
  console.group('InputComponentCallbackFunc');
    console.log(InputComponentCallbackFunc === handleClick);
  console.groupEnd();
  
  InputComponentCallbackFunc = handleClick;

  return (
    <Fieldset title='InputComponentCallback'>
      {}
      <input type='button' value={state} onClick={handleClick}/>
    </Fieldset>
  )
};

function UseCallbackComponent (){
  return (
    <Fieldset title='useCallback'>
      <InputComponentState />
      <InputComponentCallback />
    </Fieldset>
  );
};
复制代码

useMemo(function,[])

  • 和useCallback基本一致,我还没发现有什么区别Q。Q
function MemoizedLet({num}){
  const memo = useMemo(()=> num,[]);

  return (
    <Fieldset title='MemoizedLet'>
      <p>记忆 num > {memo}</p>
      <p>原始 num > {num}</p>
    </Fieldset>
  )
};

function MemoizedConst({num}){
  const memo = useMemo(()=> num,[num]);

  return (
    <Fieldset title='MemoizedConst'>
      <p>记忆 num > {memo}</p>
      <p>原始 num > {num}</p>
    </Fieldset>
  )
};

function useMemoComponent(){
  const [num,setNum] = useState([1,2,3]);

  useEffect(()=>{
    setTimeout(()=>{
      setNum([3,4,5]);
    },3000);
  },[]);

  return (
    <Fieldset title='useMemoComponent'>
      <MemoizedLet num={num} />
      <MemoizedConst num={num}/>
    </Fieldset>
  )
};
复制代码

useRef()

  • 返回一个ref对象,这个对象将维持在组件的整个生命周期
function useRefComponent(){
  const inputEl = useRef(null);
  
  function handleClick(){
    inputEl.current.focus();
  }
  return(
    <Fieldset title='useRef'>
      <input type='input' placeholder='useRef' ref={inputEl} />
      <input type='button' value='获取焦点' onClick={handleClick} />
    </Fieldset>
  )
};
复制代码

useImperativeMethods(ref,()=>({}))

  • 向父组件公开ref实例
  • 只能应用于forwardRef(props,ref)
function FancyInput(props,ref){
  const inputRef = useRef(null);
  useImperativeMethods(ref,()=>({
    focus: ()=>{
      inputRef.current.focus();
    }
  }));

  return <input type='input' placeholder='useRef' ref={inputRef}/>
};

const Input = forwardRef(FancyInput);

function UseImperativeMethodsComponent(){
  const fancyInputRef = useRef(null);
  function handleClick(){
    fancyInputRef.current.focus();
  };

  return (
    <Fieldset title='UseImperativeMethods'>
      <Input ref={fancyInputRef}/>
      <input type='button' value='获取焦点' onClick={handleClick}/>
    </Fieldset>
  )
};
复制代码

useMutationEffect(function,[])

  • 和useEffect、useLayoutEffect类似
  • useMutationEffect会在更新阶段触发
function Effect(){
  const [state,setState] = useState('请看log')
  useEffect(()=>{
    setTimeout(()=>{
      console.log('useEffect');
    });
  },[]);

  useLayoutEffect(()=>{
    setTimeout(()=>{
      console.log('useLayoutEffect');
    });
  },[]);

  useMutationEffect(()=>{
    setTimeout(()=>{
      console.log('useMutationEffect');
    });
  },[]);

  return (
    <React.Fragment>
      {state}
    </React.Fragment>
  )
}

function useMutationEffectComponent(){
  const [show,setShow] = useState(false);
  return (
    <Fieldset title='useMutationEffect'>
      { show && <Effect />}
      <input type='button' value='show' onClick={()=>setShow(!show)}/>
    </Fieldset>
  )
};
复制代码

HOC 高级组件

解释

  • 一个参数为React组件的函数,并且返回一个新函数,一个类工厂。

优点

  • 高内聚,低耦合
  • 代码复用,高度模块化

基本用途

  • 增删改props
  • 渲染劫持

公式

React.Component function WrappedComponent(React.Component:Component)
复制代码

简单例子

function WrappedComponent(Wrapped){
  return class extends React.Component{
    render(){
      const props = {
        context: 'React',
      }
      return <Wrapped {...props}/>;
    }
  }
};

class Child extends React.Component{
  render(){return <div>hello {this.props.context}</div>};
};

class App extends React.Component{
  render(){
    const HOCchild = WrappedComponent(Child);
    
    return(
      <div>
        <h1>App</h1>
        <HOCchild />
      </div>
    )
  }
};
复制代码

注意

  • 不要改变原始组件,使用组合的形式
// 错误示范
// WrappedComponent 中会覆盖组件的componentDidMount方法
// 而且使用者必须要知道被覆盖方法的具体用途,才能避免方法冲突覆盖

function WrappedComponent(Wrapped) {
  Wrapped.prototype.componentDidMount = ()=>{
    console.log('WrappedComponent');
  };

  return Wrapped;
};

class Child extends React.Component{
  componentDidMount(){
    console.log('Child');
  }
  render(){return <div>hello React</div>};
};

class App extends React.Component{
  render(){
    const HOCchild = WrappedComponent(Child);

    return(
      <React.Fragment>
        <HOCchild />
      </React.Fragment>
    )
  }
};
复制代码
// 正确示范
function wrappedComponent(Wrapped){
    return class React.Component{
        componentDibMount(){
            console.log('componentDibMount')
        }
    };
};
复制代码
  • 不要直接修改HOC的props,最好独立变量
// 错误示范
// props中可能包含太多多余的属性,无法确保高阶组件的灵活度和可重用性
function WrappedComponent(Wrapped){
    return class React.Component{
        return <Wrapped {...this.props}/>
    }
}
复制代码
// 正确
function WrappedComponent(Wrapped){
    return class React.Component{
        const {detail} = this.props;
        return <Wrapped {...detail} />
    }
}
复制代码
  • 包装显示名称,便于调试
function WrappedComponent(Wrapped){
  class WithSubscription extends React.Component{
    componentDidMount(){
      console.log('WrappedComponent');
    }
    render(){return <Wrapped/>}
  };

  function getDiplayName(W){
    return W.displayName || W.name || 'Component';
  }

  WithSubscription.displayName = `WithSubscription(${getDiplayName(Wrapped)})`

  return WithSubscription;
};
复制代码
  • 不要在render函数中使用高阶组件
TODO
复制代码
  • 必须将静态方法做拷贝
TODO
复制代码
  • Refs属性不能传递
TODO
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro JavaScript Techniques

Pro JavaScript Techniques

John Resig / Apress / 2006-12-13 / USD 44.99

Pro JavaScript Techniques is the ultimate JavaScript book for the modern web developer. It provides everything you need to know about modern JavaScript, and shows what JavaScript can do for your web s......一起来看看 《Pro JavaScript Techniques》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器