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 (
      <input ref={this.inputElement}></input>
    )
  }
}

二、代码分割

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

三、错误边界

  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 <h2>something went wrong</h2>
    }
    return this.props.children
  }
}

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

四、context

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

const Toolbar = () => {
  return (
    <ThemesButtons />
  )
}

class ThemesButtons extends React.Compenent {
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}
  • 深层组件提升(对组件的控制反转),避免中间冗余组件的props传递
function Page(props) {
  const user = props.user;
  const userLink = (
    <Link href={user.permalink}>
      <Avatar user={user} size={props.avatarSize} />
    </Link>
  );
  return <PageLayout userLink={userLink} />;
}

// 现在,我们有这样的组件:
<Page user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<PageLayout userLink={...} />
// ... 渲染出 ...
<NavigationBar userLink={...} />
// ... 渲染出 ...
{props.userLink}

五、Ref转发(未完待续)

const ref = React.createRef()
<FancyButton ref={ref}></ FancyButton>

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

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

六、Fragments

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

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

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

七、高阶组件(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 <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}
  • 避免在高阶组件内直接修改原始组件,而是使用组合的方式
// 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 <WrappedComponent {...this.props} />;
    }
  }
}

八、与第三方库协同

九、深入JSX

  • JSX是React.createElement语法糖
<MyButton color="blue" shadowSize={2}>
  Click Me
</MyButton>

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

const MyComponents = {
  DatePicker: function DatePicker(props) {
    return <div>Imagine a {props.color} datepicker here.</div>;
  }
}

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

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

function Story(props) {
  // 错误!JSX 类型不能是一个表达式。
  return <components[props.storyType] story={props.story} />;
}
function Story(props) {
  // 正确!JSX 类型可以是大写字母开头的变量。
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}
  • JavaScript表达式可以作为标签属性
  • 字符串字面量也可以作为标签属性
  • 标签属性默认值是true
  • 标签属性可以使用展开运算符
function App2() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return <Greeting {...props} />;
}
  • JavaScript表达式可以作为子元素
<MyComponent>{msg}</MyComponent>
function TodoList() {
  const todos = ['finish doc', 'submit pr', 'nag dan to review'];
  return (
    <ul>
      {todos.map((message) => <Item key={message} message={message} />)}
    </ul>
  );
}
  • 函数也可以作为子元素
function ListOfTenThings() {
  return (
    <Repeat numTimes={10}>
      {(index) => <div key={index}>This is item {index} in the list</div>}
    </Repeat>
  );
}
  • 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 (
      <div onClick={this.handleClick}>
        <p>Number of clicks: {this.state.clicks}</p>
        <p>
          Open up the browser DevTools
          to observe that the button
          is not a child of the div
          with the onClick handler.
        </p>
        <Modal>
          <Child />
        </Modal>
      </div>
    );
  }
}

function Child() {
  // 这个按钮的点击事件会冒泡到父元素
  // 因为这里没有定义 'onClick' 属性
  return (
    <div className="modal">
      <button>Click</button>
    </div>
  );
}

ReactDOM.render(<Parent />, appRoot);
  • 还有一些知识,例如refs回调,hook如何使用等未做记录。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值