当我们应用比较庞大的时候, 又或者说当你当前组件的文件比较大,我们可以将组件进行赖加载, 在某些情况下, 它才会被加载使用, 这个时候 我们可以使用 React.lazy
配合 Suspense
使用
import React, { Suspense } from 'react'; const LazyPage = React.lazy(() => import('../lazy/Lazy')); function MyLazyPage() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <LazyPage /> </Suspense> </div> ); } export default MyLazyPage 复制代码
以上代码, lazyPage组件 将会 懒加载, 在加载过程中, 将会显示 Suspense
定义的fallback 组件
配合Route使用
import React, { Suspense, lazy } from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; const Home = lazy(() => import('./routes/Home')); const About = lazy(() => import('./routes/About')); const App = () => ( <Router> <Suspense fallback={<div>Loading...</div>}> <Switch> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> </Switch> </Suspense> </Router> ); 复制代码
React.lazy 目前只支持==默认导出==(default exports), 如果你想通过==命名导出的方式==
可以参考官方这个例子
默认导出, 就是你导出的时候, 导出的数据你可以自己想怎么命名都可以, 但是命名导出, 你导出的名称必须一致
当你某个属性会在多个组件中使用到的时候, 我们可以考虑使用Context, 它可以避免你一层层地往下传递值, 它有点类似于Vue当中 Provider和 inject
要想实现这个功能,我们首先得创建一个context对象
你可以通过 React.createContext(初始的值)
来 创建一个 context 对象, 它还会返回 Provider
和 Consumer
对象, 它们都是组件, 你可以使用它,
const nameContext = React.createContext('evanyou') // 创建一个context, 默认值为evanyou 复制代码
Provider 接受一个value 值, value 值可以更新初始值的, 当value 值发生变化时, 它底下的所有组件都会重新渲染, Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数
<nameContext.Provider value="yaojin"> <Second /> <OtherPage /> </nameContext.Provider> 复制代码
将默认的evanyou 值更新成yaojin, 需要注意的是, 尽量不要将值写在value中, 如果value 是一个对象 那么都会不断返回一个新的对象, 其底下的组件都会重新渲染, 此时可以考虑将该值放到父组件的state 中
如何读取呢 ?
通过 contextType 属性 可以获取到离最近的那个匹配的 Provider 中读取context的值 Provider
重新渲染,
class Second extends React.Component { static contextType = nameContext render() { return <div> {this.context} <Transmit value={this.context}/> </div> } } 复制代码
==contextType 属性== 仅用在 class 组件中
那在函数组件应该如何读取我需要的值呢 ? 我们还可以使用 Consumer
, 在其内部, 你需要使用一个函数来接受context的值, 这个函数应该做为子元素
function OtherPage() { return <nameContext.Consumer> {value => <div >{value} </div> } </nameContext.Consumer> } 复制代码
有时候,我们想操作真实的DOM元素,例如获得某个input的焦点, 或者说或许某个组件的实例, ref 可以帮我们实现这一点
通过 React.createRef()
创建一个ref
在元素或者组件中通过 ref属性
来绑定对应的ref
==ref 只能用于 在 class 组件中使用, 不能再函数组件中使用==
class GetDom extends React.Component { constructor(props) { super(props) // 创建一个ref this.inputRef = React.createRef() this.focusTextInput = this.foucusInput.bind(this) } foucusInput() { console.log('父组件通过ref拿到该组件实例, 直接调用这个方法了') // 通过current可以访问到ref绑定的dom元素或者class组件 this.inputRef.current.focus() } render() { return ( <div> {/* ref获取inputDom元素 */} <input ref={this.inputRef} /> </div> ) } } export default class GetClass extends React.Component { constructor (props) { super(props) this.classRef = React.createRef() } componentDidMount() { this.classRef.current.foucusInput() } render () { return ( <div> {/* ref获取GetDom组件实例 */} <GetDom ref={this.classRef} /> </div> ) } } 复制代码
不同于通过React.createRef()
你还可以通过 ==回调 Refs== 来存储 ref, 在需要使用的时候, 调用它
export default class GetClass extends React.Component { constructor (props) { super(props) this.refInfo = null this.cacheRef = refData => { this.refInfo = refData; } this.triggerChildrenFun = () => { if (this.cacheRef) { this.refInfo.foucusInput () } } } componentDidMount() { this.triggerChildrenFun() } render () { return ( <div> {/* ref获取GetDom组件实例 */} <GetDom ref={this.cacheRef} /> </div> ) } } 复制代码
render 需要 创建多个元素的时候,都需要使用 div 来包裹, 如果不想使用这个div来包裹元素, 可以使用 Fragments
, 它类似于一个<>
的标签包裹你的元素, 它不支持key 或者 传递属性
如果你想使用key 属性 , 你可以显示得使用 <React.Fragment>
包裹元素
Hoc在React中称为高阶组件, 高阶组件的作用在于一些公用逻辑放到高阶组件中, 它本质上其实就是一个函数组件,但是它接受另外的组件作为参数, 并返回该组件
通过高阶组件, 我们可以把公用的方法以及state都提取都高阶组件中, 在通过props的方式传递到对应的组件中,
function HocWrap(title) { return WrapComponent => { return class extends React.Component { // 修改在 React-devtool 中高阶组件名称 static displayName = `HocWrap(${WrapComponent})` constructor(prosp) { super(prosp) this.state = { username: '', password: '', rePassword: '', } this.onChange = this.onChange.bind(this) this.composeChange = this.composeChange.bind(this) } onChange(stateName, stateValue) { this.setState({ [stateName]: stateValue, }) } handleSubmit = e => { e.preventDefault() const { username, password, rePassword } = this.state if (rePassword) { alert( `用户名: ${username}, 密码: ${password}, 确认密码: ${rePassword}`, ) } else { alert(`用户名: ${username}, 密码: ${password}`) } } composeChange(name) { return e => this.onChange(name, e.target.value) } render() { // 抽离出公用的方法 const mapFunToProps = { composeChange: this.composeChange, handleSubmit: this.handleSubmit, } return ( <div> <h1>{title}</h1> <WrapComponent {...this.state} {...mapFunToProps} /> </div> ) } } } } 复制代码
在高阶组件中, 如果你想拿到 被包装 组件的实例, 你可以使用ref转发 来实现, React 提供了一个 React.forwardRef
方法来创建一个组件, 这个方法, 不但可以接受props, 还可以接受ref作为参数, 然后我们就可以 通过 React.createRef
来创建 ref , 将这个ref 传递到 React.forwardRef
方法中
// Hoc组件内部是一个函数组件 最终返回一个React.forwardRef创建的组件, 它里面返回的是高阶组件中被包裹的组件, 并将ref作为props进行传递 return React.forwardRef((props, ref) => { return <HocRef forwardedRef={ref} /> }) 复制代码
因为高阶组件是函数组件, 我们需要通过一个层class 包裹, 因为ref 只有 class 组件可以使用, 只有它可以创建一个ref, 创建完之后我们才可以通过 React.forwardRef
让函数组件接受到这个ref, 有了这个ref ,我们就可以通过props的形式传递到内部的包裹组件了, 可以看上面的代码, 我们将ref, 通过props 传递给包裹组件了, 那么内部包裹组件就可以在通过props读取到ref, 通过ref属性 进行绑定关联了
==只需要明确 ref 只有在class组件中创建并且使用, ref不能像props一样传递给子组件, 所以当遇到函数组件的时候, 我们想拿到这个ref,就可以通过React.forwardRef来接收ref, 又或者说你想将这个ref往下传递==
// 向外暴露的是高阶组件的返回值~包装了 Register 组件返回了一个新组件 const Hoc = HocWrap('注册')(Register) export default class Wrap extends React.Component { render() { const ref = React.createRef() return ( <div> <Hoc ref={ref}></Hoc> </div> ) } } 复制代码
ReactDOM.createPortal 可以让元素插入到别的dom节点上, 不是单单地插入到父节点上
ReactDOM.createPortal(child, container) 复制代码
第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 DOM 元素