React高级特性(更新中...)

668 阅读4分钟

1、非受控组件

ref

defaultValue defaultChecked

手动操作DOM元素

2、Portals

类似于css的hack解决兼容的技巧

Portals使用场景

overflow:hidden

父组件z-index值太小

fixed需要放在body第一层级

使用方法

//父组件调用
<PortalsDemo>Modal 内容</PortalsDemo>

//子组件内容
render() {
        // // 正常渲染
        // return <div className="modal">
        //     {this.props.children} {/* vue slot */}
        // </div>

        // 使用 Portals 渲染到 body 上。
        // fixed 元素要放在 body 上,有更好的浏览器兼容性。
        return ReactDOM.createPortal(
            <div className="modal">{this.props.children}</div>,
            document.body // DOM 节点
        )
    }

效果如下

3、Context

公共信息(语言、主题)如何传递给每个组件?

用props太繁琐

用redux小题大做

三部:产生->修改->消费

1、上下文的应用三部:产生->修改->消费

1、 创建 Context 填入默认值(任何一个 js 变量)

//全局创建
const ThemeContext = React.createContext('light')

//父组件中的light变量
constructor(props) {
    super(props)
    this.state = {
        theme: 'light'
    }
}

把light这个变量放到父组件的上下文中,能功其他后代组件使用

render() {
    return <ThemeContext.Provider value={this.state.theme}>
        <Toolbar />
        <hr/>
        <button onClick={this.changeTheme}>change theme</button>
    </ThemeContext.Provider>
}

2、怎么消费这个

class组件使用方法

1、可以在组件外部指定,通过**[组件名.contextType]**指定一下对应的上下文

ThemedButton.contextType = ThemeContext
//指定 contextType 读取当前的 theme context。

2、也可以在组件内部用ES6语法 static静态属性的方式指定,

 static contextType = ThemeContext

使用

const theme = this.context
// React 会往上找到最近的 theme Provider,然后使用它的值。

函数组件怎么消费

// 底层组件 - 函数是组件
function ThemeLink (props) {
    // const theme = this.context // 会报错。函数式组件没有实例,即没有 this

    // 函数式组件可以使用 Consumer
    return <ThemeContext.Consumer>
        { value => <p>link's theme is {value}</p> }
    </ThemeContext.Consumer>

2、函数式组件的HOOKS函数的应用

useState:在函数组件中应用状态

useEffect:在函数组件中应用周期函数

useRef:在函数组件中操作DOM 连接地址 blog.csdn.net/weixin_4167…

3、函数组件应用上下文

连接地址

blog.csdn.net/weixin_4167…

4、异步组件

import()

React.lazy

React.Suspense

const ContextDemo = React.lazy(() => import('./ContextDemo'))

class App extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <div>
            <p>引入一个动态组件</p>
            <hr />
            <React.Suspense fallback={<div>Loading...</div>}>
                <ContextDemo/>
            </React.Suspense>
        </div>

        // 1. 强制刷新,可看到 loading (看不到就限制一下 chrome 网速)
        // 2. 看 network 的 js 加载
    }
}

5、性能优化

1、性能优化在React更加重要,相对于vue更加重要

shouldComponentUpdata(简称SCU)

shouldComponentUpdata是可选的生命周期,默认返回值是true,即在React中默认:父组件有更新,子组件则无条件也更新

SCU一定要配合不可变值

React为什么要弄这么一个可选 可定制的生命周期,为什么不直接封装好呢

class Footer extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <p>
            {this.props.text}
            {this.props.length}
        </p>
    }
    componentDidUpdate() {
        console.log('footer did update')
    }
    shouldComponentUpdate(nextProps, nextState) {
        if (nextProps.text !== this.props.text
            || nextProps.length !== this.props.length) {
            return true // 可以渲染
        }
        return false // 不重复渲染
    }

    // React 默认:父组件有更新,子组件则无条件也更新!!!
    // 性能优化对于 React 更加重要!
    // SCU 一定要每次都用吗?—— 需要的时候才优化
}

父组件调用

render() {
        return <div>
            <Input submitTitle={this.onSubmitTitle}/>
            <List list={this.state.list}/>
            <Footer text={this.state.footerInfo} length={this.state.list.length}/>
        </div>
    }
SCU使用新的state和旧的state做比较,返回值truefalse又决定了是否重新渲染
默认返回ture,即默认不相等,都重新渲染
相同则返回false,不渲染

2、PureComponent和React.memo

PureComponent用在class组件

React.memo用在函数组件

PureComponent,SCU中实现了浅比较

纯组件,浅比较,如果前后state相同,则返回false,不渲染

如果不相同,则返回true,重新渲染

也是配合不可变值来使用

浅比较已经使用大部分情况,尽量不要做深度比较,深度比较,一次性递归到底,性能不好

深度比较用

import _ from 'lodash'
//子组件
class List extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        const { list } = this.props

        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>
    }

    // 增加 shouldComponentUpdate
    shouldComponentUpdate(nextProps, nextState) {
        // _.isEqual 做对象或者数组的深度比较(一次性递归到底)
        if (_.isEqual(nextProps.list, this.props.list)) {
            // 相等,则不重复渲染
            return false
        }
        return true // 不相等,则渲染
    }
}

父组件

    render() {
        return <div>
            <Input submitTitle={this.onSubmitTitle}/>
            <List list={this.state.list}/>
        </div>
    }
    onSubmitTitle = (title) => {
        // 正确的用法
        this.setState({
            list: this.state.list.concat({
                id: `id-${Date.now()}`,
                title
            })
        })

        // // 为了演示 SCU ,故意写的错误用法
        // this.state.list.push({
        //     id: `id-${Date.now()}`,
        //     title
        // })
        // this.setState({
        //     list: this.state.list
        // })
    }

3、不可变值immutable.js

6、高级组件HOC

7、Render Props