React 高级篇学习

React 高级篇学习

一、无障碍

  1. 无障碍体现在:HTML语义化、无鼠标场景的兼容、无障碍表单等等。WCAG(网页内容无障碍指南)有相关标准
  2. react提供 支持碎片化标签的包裹,同时不影响语义(应该同vue slot标签一样并不会被渲染出来)
  3. 无障碍表单react的jsx的语法 使用 “htmlfor” 代替 原生html的 “for”
  4. react运行过程中可能会失焦,需要通过编程式方式控制焦点,react获取元素的方式是通过以下方式
class hello extend React.Compenent {
  constrctor(props){
    super(props)
    this.inputElement = React.createRef()
  }

  render(){
    return (
      
    )
  }
}

二、代码分割

  1. 使用 lazy函数,通过动态import进行组件引入
  2. import仅支持默认导出,如果是统一暴露,需要中间模块做一个默认导出
  3. 利用 suspense 可以对异步组件做降级处理
  4. 利用react-router 中的switch route 可以对动态组件选择性地渲染
const hello = () => {
  
    loading...
}> }

三、错误边界

  1. 错误边界是一种捕获子树中出现错误的降级组件
  2. 如果在class组件中定义生命周期钩子 getDerivedStateFromError 或 componentDidCatch,此组件即为错误边界
  3. 写一个错误边界
class ErrorBoundary extends React.Compenent {
  constructor(props){
    super(props)
    this.errorState = false
  }

  static getDerivedStateFromError(error){
    return { errorState: true }
  }

  componentDidCatch(error, errorInfo) {
    // 你同样可以将错误日志上报给服务器
    logErrorToMyService(error, errorInfo);
  }
  
  render(){
    if(this.state.errorState){
      return 

something went wrong

} return this.props.children } } // 然后作为一个常规组件使用它
  1. 错误边界注意事项:
  • 只能捕获错误边界子树中的错误,本身错误并不能捕获
  • react16以后若是错误边界没捕获不到异常,则会卸载整个组件树
  • 错误边界无法捕获事件处理器异常,只能在内部通过try catch自行进行javascript捕获

四、context

  • context是为了解决 嵌套组件值传递 需要逐层传递props的问题
const ThemeContext = React.createContext('light')
class App extends react.compenent {
  render () {
    return {
      
        
      
    }
  }
}

const Toolbar = () => {
  return (
    
  )
}

class ThemesButtons extends React.Compenent {
  static contextType = ThemeContext;
  render() {
    return 
  • 深层组件提升(对组件的控制反转),避免中间冗余组件的props传递
function Page(props) {
  const user = props.user;
  const userLink = (
    
      
    
  );
  return ;
}

// 现在,我们有这样的组件:

// ... 渲染出 ...

// ... 渲染出 ...

// ... 渲染出 ...
{props.userLink}

五、Ref转发(未完待续)

const ref = React.createRef()


const FancyButton = React.forwardRef((props, ref) => {
  return (
    
  )
})

console.log(ref.current) // 为button这个dom元素

六、Fragments

class Columns extends React.Component {
  render() {
    return (
      
        Hello
        World
      
    );
  }
}
// 或者
class Columns extends React.Component {
  render() {
    return (
      <>
        Hello
        World
      
    );
  }
}

key 是唯一可以传递给 Fragment 的属性。

function Glossary(props) {
  return (
    
{props.items.map(item => ( // 没有`key`,React 会发出一个关键警告
{item.term}
{item.description}
))}
); }

七、高阶组件(HOC)

  • 高阶组件是一个函数,入参是一个react组件和其他数据,返回值是一个包装之后的组件(目的是对相同逻辑组件封装)
  • 优势:将业务逻辑抽象集中起来,与数据解耦。
// CommentListWithSubscription 和 BlogPostWithSubscription具有相同业务逻辑,只是数据不同,那么通过withSubscription传入不同的数据即可。
const CommentListWithSubscription = withSubscription(
  CommentList,
  (DataSource) => DataSource.getComments()
);

const BlogPostWithSubscription = withSubscription(
  BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id)
)

// 此函数接收一个组件...
function withSubscription(WrappedComponent, selectData) {
  // ...并返回另一个组件...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: selectData(DataSource, props)
      };
    }

    componentDidMount() {
      // ...负责订阅相关的操作...
      DataSource.addChangeListener(this.handleChange);
    }

    componentWillUnmount() {
      DataSource.removeChangeListener(this.handleChange);
    }

    handleChange() {
      this.setState({
        data: selectData(DataSource, this.props)
      });
    }

    render() {
      // ... 并使用新数据渲染被包装的组件!
      // 请注意,我们可能还会传递其他属性
      return ;
    }
  };
}
  • 避免在高阶组件内直接修改原始组件,而是使用组合的方式
// no no no
function logProps(InputComponent) {
  InputComponent.prototype.componentDidUpdate = function(prevProps) {
    console.log('Current props: ', this.props);
    console.log('Previous props: ', prevProps);
  };
  // 返回原始的 input 组件,暗示它已经被修改。
  return InputComponent;
}

// 每次调用 logProps 时,增强组件都会有 log 输出。
const EnhancedComponent = logProps(InputComponent);

// yes yes
function logProps(WrappedComponent) {
  return class extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('Current props: ', this.props);
      console.log('Previous props: ', prevProps);
    }
    render() {
      // 将 input 组件包装在容器中,而不对其进行修改。Good!
      return ;
    }
  }
}

八、与第三方库协同

九、深入JSX

  • JSX是React.createElement语法糖

  Click Me


React.createElement(
  MyButton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)
  • 在 JSX 类型中可以使用点语法
import React from 'react';

const MyComponents = {
  DatePicker: function DatePicker(props) {
    return 
Imagine a {props.color} datepicker here.
; } } function BlueDatePicker() { return ; }
  • 用户定义的组件必须以大写字母开头
  • 想通过通用表达式来(动态)决定元素类型,你需要首先将它赋值给大写字母开头的变量
import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
  photo: PhotoStory,
  video: VideoStory
};

function Story(props) {
  // 错误!JSX 类型不能是一个表达式。
  return ;
}
function Story(props) {
  // 正确!JSX 类型可以是大写字母开头的变量。
  const SpecificStory = components[props.storyType];
  return ;
}
  • JavaScript表达式可以作为标签属性
  • 字符串字面量也可以作为标签属性
  • 标签属性默认值是true
  • 标签属性可以使用展开运算符
function App2() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return ;
}
  • JavaScript表达式可以作为子元素
{msg}
function TodoList() {
  const todos = ['finish doc', 'submit pr', 'nag dan to review'];
  return (
    
    {todos.map((message) => )}
); }
  • 函数也可以作为子元素
function ListOfTenThings() {
  return (
    
      {(index) => 
This is item {index} in the list
}
); }
  • false, null, undefined, and true 是合法的子元素。但它们并不会被渲染

十、通过Portal进行事件冒泡

  • portal是通过 ReactDOM.createPortal(child, container) 将元素挂载到非直接父元素dom结点的一种方式
  • portal子元素仍能够冒泡到父元素,尽管其并未挂在父元素上
  • 对下方代码做一个解释:Modal组件的子元素挂载到了新创建的div节点上,又由于child组件本身并无定义点击事件,因此会冒泡到parent组件的onclik事件,这就是如何利用portal进行事件冒泡。
// 在 DOM 中有两个容器是兄弟级 (siblings)
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el
    );
  }
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {clicks: 0};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      clicks: state.clicks + 1
    }));
  }

  render() {
    return (
      

Number of clicks: {this.state.clicks}

Open up the browser DevTools to observe that the button is not a child of the div with the onClick handler.

); } } function Child() { // 这个按钮的点击事件会冒泡到父元素 // 因为这里没有定义 'onClick' 属性 return (
); } ReactDOM.render(, appRoot);
  • 还有一些知识,例如refs回调,hook如何使用等未做记录。

你可能感兴趣的:(react.js,学习,javascript)