「这是我参与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 的体积,并延迟加载在初次渲染时未用到的组件
注意:
- 渲染 lazy组件 依赖该组件渲染树上层的 <React.Suspense> 组件。
- 需要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>
)
}