1. 组合和继承
React 有十分强大的组合模式。React推荐我们使用组合而非继承来实现组件间的代码重用。
通过 JSX
嵌套,实现组件之间的组合。
// 渲染模板
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
// 传递数据
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
2. Context
Context 灵魂三拷问?
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
// App.js
import React from 'react';
import Parent from './Parent'
export const {Provider, Consumer} = React.createContext();
export function App() {
return (
<Provider value={'li'}>
<Parent/>
</Provider>
);
}
// Parent.js
import React from 'react';
import Child from './Child'
class Parent extends React.Component {
render() {
return <Child/>
}
}
export default Parent;
// Child.js
import React from 'react';
import {Consumer} from './App'
class Child extends React.Component {
render() {
return (
<div>
<Consumer>
{
theme => <div>{theme}</div>
}
</Consumer>
</div>
)
}
}
export default Child;
- ContextType
Context会让组件变得不纯粹,因为依赖了全局变量。所以这决定了Context一般不会大规模的使用。所以一般在一个组件中使用一个Context就好。由于Consumer的特性,里面的代码必须是这个函数的返回值。这样就显得复杂与不优雅了。
- 挂载在
class
上的contextType
属性会被重赋值为一个由React.createContext()
创建的Context
对象。这能让你使用this.context
来消费最近Context
上的那个值。你可以在任何生命周期中访问到它,包括render
函数中。
import React from 'react'
import MyContext from './App.js'
class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* 基于 MyContext 组件的值进行渲染 */
}
}
MyClass.contextType = MyContext;
- 如果你正在使用实验性的 public class fields 语法,你可以使用 static 这个类属性来初始化你的
contextType
。
import React, { Component, createContext } from 'react';
const BatteryContext = createContext();
//声明一个孙组件 ,使用context.type
class Leaf extends Component {
static contextType = BatteryContext;
render() {
const battery = this.context;
return<h1>Battery : {battery}</h1>
}
}
//声明一个子组件
class Middle extends Component {
render() {
return <Leaf />
}
}
class MyClass extends Component {
state = {
battery: 60,
}
render() {
const { battery } = this.state;
return (
<BatteryContext.Provider value={battery}>
<button
type="button"
onClick={() => this.setState({ battery: battery - 1 })}
>
减减
</button>
<Middle />
</BatteryContext.Provider>
);
}
}
将Provider
的 value
状态提升到父节点的 state 里:防止当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。
3. Suspense
Suspense
使得组件可以“等待”某些操作结束后,再进行渲染。目前,Suspense
仅支持的使用场景是:通过 React.lazy
动态加载组件。它将在未来支持其它使用场景,如数据获取等。
React.lazy
React.Suspense
(3.1)、 Lazy
React.lazy
函数用来加载动态引入的组件。
React.lazy
函数能让你像渲染常规组件一样处理动态引入(的组件)。
React.lazy
接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise
,该 Promise
需要 resolve
一个 defalut export
的 React 组件。
然后应在 Suspense
组件中渲染 lazy
组件,如此使得我们可以使用在等待加载 lazy 组件时做优雅降级(如 loading 指示器等)。
fallback
属性接受任何在组件加载过程中你想展示的 React 元素。你可以将 Suspense 组件置于懒加载组件之上的任何位置。你甚至可以用一个 Suspense
组件包裹多个懒加载组件。
// parent
import React, {Suspense} from "react";
const OtherComponent = React.lazy(() => {return import('./OtherComponent.js')});
export default function LazyDemo() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent/>
</Suspense>
</div>
);
}
// child
import React from "react";
export default function OtherComponent() {
return (
<div>777</div>
)
}
(3.2)、 Suspense
React 16.6添加了一个组件,允许您“等待”一些代码加载,
suspense
是React 16.6添加了一个组件,此组件使得加载其他代码成为同步,并在等待时声明性地指定加载状态(例如spinner
)。
4. memo
React.memo
为高阶组件,主要用于性能优化。它与 React.PureComponent
非常相似,但它适用于函数组件,但不适用于 class
组件。默认情况下其只会对复杂对象做浅层对比。
// 1. 现在有一个显示时间的组件,每一秒都会重新渲染一次
import React from 'react';
export default class extends React.Component {
constructor(props){
super(props);
this.state = {
date : new Date()
}
}
componentDidMount(){
setInterval(()=>{
this.setState({
date:new Date()
})
},1000)
}
render(){
return (
<div>
<Child seconds={1}/>
<div>{this.state.date.toString()}</div>
</div>
)
}
// 2. 对于Child组件我们肯定不希望也跟着渲染,所有需要用到 PureComponent
class Child extends React.PureComponent {
render(){
console.log('I am rendering');
return (
<div>I am update every {this.props.seconds} seconds</div>
)
}
}
// 3. 现在新出了一个 React.memo() 可以满足创建纯函数 而不是一个类的需求
// 3.1 React.memo()
import React, { useState, useMemo, memo } from 'react'
export default function MemoCallback() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2;
}, [count === 5])
const Counter = memo(function Child(props) {
return (
<span>子组件:{props.count}</span>
)
});
return (
<div>
<button onClick={() => {setCount(count + 1)}}>click: {count}</button>
{/*<span>double: {double}</span>*/}
<Counter count={double}/>
</div>
)
}
// 3.2 React.memo()可接受2个参数,
// 第一个参数为纯函数的组件,第二个参数用于对比props控制是否刷新,与shouldComponentUpdate()功能类似。
import React from "react";
function Child({seconds}){
console.log('I am rendering');
return (
<div>I am update every {seconds} seconds</div>
)
};
function areEqual(prevProps, nextProps) {
if(prevProps.seconds===nextProps.seconds){
return true
}else {
return false
}
}
export default React.memo(Child,areEqual)
// 3.3 React.memo() 与Redux
import React from "react";
function Child({seconds,state}){
console.log('I am rendering');
return (
<div>
<div>I am update every {seconds} seconds</div>
<p>{state}</p>
</div>
)
};
const mapStateToProps = state => ({
state: 'React.memo()用在connect()(内)'
});
export default connect(mapStateToProps)(React.memo(Child))
5. 在 React 中有两种流行的方式来共享组件之间的状态逻辑
- 高阶组件
render props
6. React.PureComponent
React.PureComponent
与 React.Component
很相似。两者的区别在于 React.Component
并未实现 shouldComponentUpdate()
,而 React.PureComponent
中以浅层对比 prop 和 state 的方式来实现了该函数。
// 1. shouldComponentUpdate 的 demo
class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}
>
Count: {this.state.count}
</button>
);
}
}
// 2. React.PureComponent 的 demo
class ListOfWords extends React.PureComponent {
render() {
return <div>{this.props.words.join(',')}</div>;
}
}
class WordAdder extends React.Component {
constructor(props) {
super(props);
this.state = {
words: ['marklar']
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// This section is bad style and causes a bug
const words = this.state.words;
words.push('marklar');
this.setState({words: words});
}
render() {
return (
<div>
<button onClick={this.handleClick}>click</button>
<ListOfWords words={this.state.words} />
</div>
);
}
}
7. ref
ref的3中用法:
- 字符串
dom节点上使用,通过this.refs[refName]来引用真实的dom节点
<input ref="inputRef" /> //this.refs['inputRef']来访问
- 回调函数
React 支持给任意组件添加特殊属性。ref 属性接受一个回调函数,它在组件被加载或卸载时会立即执行。
当给 HTML 元素添加 ref 属性时,ref 回调接收了底层的 DOM 元素作为参数。 当给组件添加 ref 属性时,ref 回调接收当前组件实例作为参数。 当组件卸载的时候,会传入null ref 回调会在componentDidMount 或 componentDidUpdate 这些生命周期回调之前执行。
<input ref={(input) => {this.textInput = input;}} type="text" /> //HTML 元素添加 ref 属性时
<CustomInput ref={(input) => {this.textInput = input;}} /> //组件添加 ref 属性
- React.createRef()
在React 16.3版本后,使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,该ref的current属性 将能拿到dom节点或组件的实例
class Child extends React.Component{
constructor(props){
super(props);
this.myRef=React.createRef();
}
componentDidMount(){
console.log(this.myRef.current);
}
render(){
return <input ref={this.myRef}/>
}
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!