【React初接触】(五)React API

240 阅读4分钟

「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

创建 React元素

React.createElement()

// 创建并返回新的 React元素
// @type 标签名字符串 / react组件类型 / React fragment类型
React.createElement(
    type, [props], [...children]
)

可以使用 JSX 来编写 UI组件(每个JSX元素都是调用 React.createElement() 的语法糖

对比使用方法:JSX & createElement & e

JSX:构建环境需要配置 jsx编译

class Hello extends React.component {
  render() {
    return <div>Hello {this.props.toWhat}</div>
  }
}
ReactDOM.render (
	<Hello toWhat="world" />,
  document.getElementById('root')
)

createElement

class Hello extends React.component {
  render() {
    return react.createElement('div', null, `Hello ${this.props.toWhat}`)
  }
}
React.render (
    react.createElement(Hello, {toWhat: 'world'}, null),
  document.getElementById('root')
)

e - 快捷方式

const e = React.createElement
React.render (
    e(div, null, 'Hello World'),
  document.getElementById('root')
)

转换元素

cloneElement()

React.cloneElement(element, props, [...children])

import React from 'react'

export default function App {
  const Clone = React.cloneElement(<Temp/>, { key: 12, name: '张三' }, <div>你好,世界1</div>)
  return (
  	<div>{Clone}</div>
  )
}

const Temp = (props) => {
  return (
  	<div>
    	<span>你好世界,{props.name}</span>
      {props.children}
    </div>
  )
}

/*
你好世界,张三
你好,世界1
*/

部分源码


import invariant from 'shared/invariant';

export function cloneElement(element, config, children) {
  
  // ---- 1. 对元素非空校验 ------
  invariant(
    !(element === null || element === undefined),
    'React.cloneElement(...): The argument must be a React element, but you passed %s.',
    element,
  );
  
  
	// ---- 2. 声明 props 、 key 、 ref 、 self 、 source 、 owner 根据 element 赋默认值 ------
  const props = Object.assign({}, element,props)
  let key = element.key
  let ref = element.ref
  const self = element._self
  const source = element._source
  let owner = element._owner
  
  // ----- 3. 判断config是否为空 ------
  if (config != null) {
    // 在config属性中,查找key 和 ref。如果有,单独赋值,更新原来声明的 key 和 ref
    if (hasValidRef(config)) {
      ref = config.ref;
      owner = ReactCurrentOwner.current;
    }
    if (hasValidKey(config)) {
      key = '' + config.key;
    }
    
    // ----- 5. 遍历config -------
    let defaultProps;
    // 收集默认参数
    if (element.type && element.type.defaultProps) {
      defaultProps = element.type.defaultProps;
    }
    for (propName in config) {
       // 检测不是原型链继承属性 且 属性名不是 key、ref、__source、__self
       if (
         hasOwnProperty.call(config, propName) &&
         !RESERVED_PROPS.hasOwnProperty(propName)
       ) {
         // 检测属性 等于undefined 且 defaultProps不等于 undefund,将 defaultProps 中属性名的属性值给props
         if (config[propName] === undefined && defaultProps !== undefined) {
           props[propName] = defaultProps[propName];
         }
         // 否则将 config 中此属性名的属性值赋给props
         else {
           props[propName] = config[propName];
         }
       }
    }	
  }
  
  // ------- 6. 遍历剩余参数,作为新元素的children -------
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
  }
  return ReactElement(element.type, key, ref, self, source, owner, props);
}

isValidElement()

React.isValidElement(object)

校验入参是否为合法的react元素,返回一个布尔值

React.Children

# 【面试问到就是不会系列】React.Children与React.cloneElement杂谈

对于 props 中的 children 属性,由于无法判断其具体类型,不能直接使用 数组方法进行处理。React.children 可以用于处理 this.props.children 的不透明数据结构。

  • React.Children.map(props.children, child => {})

    props.children为null / undefined 时, 原值返回,否则返回数组

    如果 children 是一个 Fragment 对象,它将被视为单一子节点的情况处理,而不会被遍历

  • React.Children.forEach(props.children, child => {})

    和 map 类似处理,但是无返回值

  • React.Children.count(props.children)

    返回内部元素数,和上面两个迭代方法的回调触发次数相同

  • React.Children.only(props.children)

    判断传入 children 是否只有一项。注意接收类型为 React element。

  • React.Children.toArray(props.children)

    将 props.children 扁平化成数组的形式返回,并给每个子节点分配一个key。toArray 会为返回数组中的每个 key 添加前缀,以使得每个元素 key 的范围都限定在此函数入参数组的对象内。

Fragment

React.Fragment

React.Fragment 组件能够在不额外创建 DOM 元素的情况下,让 render() 方法中返回多个元素。

简写:<> ... </>

Ref

React.createRef

创建一个能够通过 ref 属性附加到 React 元素的ref。

const inputRef = React.craeteRef<any>()
MyInput = (e: any) => {
console.log(e.target.value)
this.inputRef.current.focus();
}
MyFocus = () => {
console.log('my focus --- ')
}
​
class My extends React.component {
  render() {
 return (
   <input type="text" ref={this.inputRef} onInput={this.MyInput} onfocus={this.MyFocus}></input>
 )
}
}

React.forwardRef

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

创建一个React组件,这个组件能够将其接受的ref属性转发到其组件树下的另一个组件中

常用场景:

  • 转发 refs 到 DOM组件
  • 在高阶组件中转发 refs
const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
))
​
const ref = React.createRef()
<FancyButton ref={ref}>Click me!</FancyButton>
​
​
/*
1. React将 <FancyButton ref={ref}> 元素的ref 作为第二个参数传递给 React.forwardRef 函数中的渲染函数
2. 渲染函数将 ref 传递给 <button ref={ref}>
​
=> 当 React 附加了 ref属性 后,ref.current 将直接指向 <button>DOM元素实例
*/

Suspense

使得组件可以等待某些操作完成之后,再进行渲染。目前仅支持使用场景:通过 React.lazy 动态加载的组件

React.lazy

定义一个动态加载的组件

用途:有助于缩减 bundle 的体积,并延迟加载在初次渲染时未用到的组件

注意:

  1. 渲染 lazy组件 依赖该组件渲染树上层的 <React.Suspense> 组件。
  2. 需要JS环境支持Promise。IE11以下版本浏览器需要通过引入polyfill来使用。

如何使用:代码分割文档

React.Suspense

指定加载指示器(loading indicator),以防其组件树中的某些子组件尚未具备渲染环境。

// 该组件动态加载
const SomeComponent = React.lazy(() => import('./SomeComponent'))
​
function MyComponent() {
  return (
    // 显示 Spinner组件 直到 SomeCompnent 加载完成
    <React.Suspense fallback={<Spinner/>}>
      <div>
        <SomeComponent/>
      </div>
    </React.Suspense>
  )
}