React的记录

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

内容简介: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
复制代码

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

查看所有标签

猜你喜欢:

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

七周七语言(卷2)

七周七语言(卷2)

【美】Bruce A. Tate(泰特)、Fred Daoud(达乌德)、Ian Dees(迪斯) / 7ML翻译组 / 人民邮电出版社 / 2016-12 / 59

深入研习对未来编程具有重要意义的7种语言 Lua、Factor、Elixir、Elm、Julia、Idris和MiniKanren 本书带领读者认识和学习7种编程语言,旨在帮助读者探索更为强大的编程工具。 本书延续了同系列的畅销书《七周七语言》《七周七数据库》和《七周七Web开发框架》的体例和风格。 全书共8章,前7章介绍了Lua、Factor、Elm、Elixir、Jul......一起来看看 《七周七语言(卷2)》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具