美文网首页
React 重读- 高阶用法

React 重读- 高阶用法

作者: 梁坤同学 | 来源:发表于2019-11-09 14:18 被阅读0次

React 重读- 高阶用法

核心概念

正确使用 state

  1. 不要直接修改 state, 而是使用 setState() 的方式去更新,否则组件不会重新渲染;
  2. state 的更新可能是异步的,所以不要依赖他们的值来更新下一个状态。可以让 setState() 接受一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 作为第二个参数:
this.setState((state, props) => {
  return {
    counter: state.counter + props.increment
  }
})
  1. state 的更新会被合并,可以分别调用 setState() 来单独更新它们。

事件处理

  1. 在 react 中,不能通过返回 false 的方式阻止默认行为,必须显式的使用 preventDefault

     handleClick(e) {
       e.preventDefault()
       console.log("The link was clicked.)
     }
    
  2. 绑定 this 的几种方式

    • 在 constructor 中绑定 this
    constructor(props) {
      super(props);
      this.state = {};
      this.handleClick = this.handleClick.bind(this)
    }
    
    • 在事件调用的使用绑定 this
    <Button onClick={this.handleClick.bind(this)} />
    
    • 使用箭头函数
    handleClick = () => {}
    
    • 使用 lodash-decorators 的 Bind 方法
    import { Bind } from 'lodash-decorators'
    ...
    
    @Bind()
    handleClick() {}
    
  3. 向事件处理程序传参

    // 这两种方式时等价的
    <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
    <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
    
  4. 阻止组件渲染

render 方法直接返回 null, 则组件就不会有任何渲染。在组件的 render 方法中返回 null 并不会影响组件的生命周期。

受控组件

state 是"唯一数据源",并且只能通过 setState() 来更新。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做受控组件

状态提升

在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state。这就是所谓的状态提升

高级指引

Context

Context 设计目的是为了共享那些对于一个组件树而言是"全局"的数据,例如当前认证的用户、主题或首选语言。
如果只是想要避免层层传递一些属性,组件组合(component composition)有时候是一个 context 更好的解决方案。

const MyContext = React.createContext(defaultValue)
<MyContext.Provider value={/* 某个值 */} />

undefined 传递给 Provider 的 value 时,组件的 defaultValue 不会生效。
当 Provider 的 value 值发生变化时,它内部的组件都会重新渲染,并且不受制于 shouldComponentUpdate 函数。
context 会使用参考标识(reference identity)来决定何时进行渲染,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中出发以外的渲染。例如,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumer 组件,因为 value 属性总是被赋值为新的对象:

class App extends React.Component {
  render() {
    return (
      <Provider value={{ something: 'something }}>
        <Toolbar />
      </Provider>
    )
  }
}

为了防止这种情况,将 value 状态提升到父节点的 state里:

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      value: { something: 'something },
    }
  }

  render() {
    return (
      <Provider value={this.state.value}>
        <Toolbar />
      </Provider>
    )
  }
}

一个组件消费多个 context

function Content(theme, user) {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {User => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  )
}

错误边界

部分 UI 组件内的 JavaScript 错误会导致 React 的内部状态被破坏,并且在下一次渲染时产生可能无法追踪的错误,甚至导致整个应用崩溃。
为了这个问题, React 引入了错误边界——一种React组件, 这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且,它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。
错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。

错误边界无法捕获的错误

  • 事件处理
  • 异步代码
  • 服务端渲染
  • 它自身抛出来的错误(并非它的子组件)

错误边界使用

当组件中定义了 static getDerivedStateFromError()componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,就会编程一个错误边界。使用static getDerivedStateFromError() 渲染备用 UI,使用 componentDidCatch() 打印错误信息。

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false
    };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    console.log(error, info);
  }

  render() {
    if (this.state.hasError) {
      // 自定义降级后的 UI 并渲染
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

<ErrorBoundary>
  <MyWidget>
</ErrorBoundary>

错误边界得只针对 React 组件,只有 class 组件才可以成为错误边界组件。错误边界尽可以捕获其子组件得错误,无法捕获自身得错误。如果一个错误边界无法渲染错误信息,则错误会冒泡至最近的上层错误边界,这也类似于 JavaScript 中 catch {} 的工作机制。

Refs 转发

Ref 转发是一项将 ref 自动地通过组件传递给其一子组件的技巧。
Ref转发是一个可选特性,其允许某些组件接受 ref,并将其向下传递(换句话说,"转发"它)给子组件。

Refs 使用

  • 通过调用 React.createRef 创建一个 React ref 并将其赋值给 ref 变量。
  • React 传递 refforwardRef 内函数 (props, ref) => ...., 作为其第二个参数。
  • 当 ref 挂载完成,ref.current 将指向 <button> DOM 节点
const FancyButton = React.forwardRef((props, ref) => {
  console.log("props", props);
  console.log("ref", ref);

  return <button ref={ref}>{props.children}</button>;
});

// 你可以直接获取 DOM button 的 ref
const ref = React.createRef();

export default function MyDemo () {
  // return <FancyButton ref={ref}>Click me!</FancyButton>;
  return <FancyButton ref={ref} children="Click me!" />;
};

高阶组件(HOC)中转发 refs

如果你对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件

const FancyButton = React.forwardRef((props, ref) => {
  return <button ref={ref}>{props.children}</button>;
});

// 你可以直接获取 DOM button 的 ref
const ref = React.createRef();

function MyDemo () {
  // return <FancyButton ref={ref}>Click me!</FancyButton>;
  return <FancyButton ref={ref} children="Click me!" />;
};

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console("old props", prevProps)
      console("new props", this.props)
    }

    render() {
      const { forwardRef, ...rest } = this.props
      return <Component ref={forwardRef} {...rest} />
    }
  }
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardRef={ref} />
  })
}

export default logProps(MyDemo)

DevTool 中显示自定义名称

  • <LogProps {...props} forwardedRef={ref} />
  • forwardRef.displayName = name;

Fragments

使用显式 <React.Fragment> 语法声明的片段可能具有 key,key 是唯一可以传递给 Fragment 的属性

高阶组件(HOC)

  • 高阶组件是参数为组件,返回值为新组件的函数。它是一种基于 React 的组合特性二型策划给你的设计模式。
  • 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。
  • Hoc是纯函数,没有副作用。它可以与其他 HOC 组合,甚至可以与其自身组合。
  • 可以将 HOC 视为可参数化容器组件。容器组件担任分离将高层和底层关注的责任,由容器管理订阅和状态,并将 props 传递给处理渲染 UI。HOC 使用容器作为其实现的一部分。


    容器组件和展示组件

约定

  • 不要在 HOC 中修改组件原型, 或以其他形式修改它。而应该使用组合的方式,通过将组建包装在容器组件实现功能。

  • HOC 为组件添加特性,自身不应该大幅改变约定,HOC 返回的组件与原组件应保持类似的接口。

  • 最大化可组合性

  • 包装显示名称以便轻松调试

Render Props

render props 是指一种在 React 组件之间使用一个值为函数的 props 共享代码的技术。具有 render pros的组件接受一个函数,该函数返回一个 React 元素并调用它而不是实现自己的渲染逻辑。

class Cat extends Component {
  render() {
    const mouse = this.props.mouse
    return (
      <img 
        src="image.png" 
        style={{ position: 'absolute', left: mouse.x, top: mouse.y, width: '100px' }} />
    )
  }
}

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

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

  render() {
    return (
      <div style={{ height: "100%" }} onMouseOver={this.handleMouseMove}>
        { this.props.render(this.state) }
      </div>
    );
  }
}


export default class MouseTracker extends Component {
  render() {
    return (
      <div>
        <h1>移动鼠标!</h1>
        <Mouse render={mouse=> (
          <Cat mouse={mouse} />
        )} />
      </div>
    )
  }
}

相关文章

  • React 重读- 高阶用法

    React 重读- 高阶用法 核心概念 正确使用 state 不要直接修改 state, 而是使用 setStat...

  • react高阶组件

    React进阶之高阶组件 前言 本文代码浅显易懂,思想深入实用。此属于react进阶用法,如果你还不了解react...

  • 利用 React 高阶组件实现一个面包屑导航

    什么是 React 高阶组件 React 高阶组件就是以高阶函数的方式包裹需要修饰的 React 组件,并返回处理...

  • React 进阶之高阶组件

    高阶组件 HOC 高阶组件(HOC)是react中的高级技术,用来重用组件逻辑。但高阶组件本身并不是React A...

  • 高阶组件

    React 高阶组件HOC (Higher-Order Component) 高阶组件是react中重复使用组件逻...

  • React高阶组件

    1、React高阶组件

  • react 笔记

    react 基本概念解析 react 的组件声明周期 react 高阶组件,context, redux 等高级...

  • 探索Vue高阶组件

    探索Vue高阶组件 高阶组件(HOC)是 React 生态系统的常用词汇,React 中代码复用的主要方式就是使用...

  • React高阶组件(HOC)

    高阶组件(Higher-Order Components) 高阶组件(HOC)是 React 中用于重用组件逻辑的...

  • [React Hooks] 样例学习---useWhyDidYo

    前置知识 React.memo React.memo 是一个高阶组件。类似于 React.PureComponen...

网友评论

      本文标题:React 重读- 高阶用法

      本文链接:https://www.haomeiwen.com/subject/yiqnbctx.html