[React] Hook - extension & optimization


import React, { Component } from 'react'

export default class Demo extends Component {

    state = {count:0}

    add = ()=>{
        // [1] 对象式的setState
        /* //1.获取原来的count值
        const {count} = this.state
//2.更新状态 this.setState({count:count+1},()=>{ console.log(this.state.count); }) //console.log('12行的输出',this.state.count); //0 因为是异步 update state value,所以这不是正确的方法
*/ // [2] 函数式的setState this.setState( state => ({count:state.count+1})) // 只需要一步,一行。 } render() { return ( <div> <h1>当前求和为:{this.state.count}</h1> <button onClick={this.add}>点我+1</button> </div> ) } }

  

import React, { Component,lazy,Suspense} from 'react'
import {NavLink,Route} from 'react-router-dom'

// import Home from './Home'
// import About from './About'

import Loading from './Loading'
const Home  = lazy( ()=> import('./Home')  )
const About = lazy( ()=> import('./About') )

export default class Demo extends Component {
    render() {
        return (
            <div>
                <div className="row">
                    <div className="col-xs-offset-2 col-xs-8">
                        <div className="page-header"><h2>React Router Demo</h2></div>
                    </div>
                </div>
                <div className="row">
                    <div className="col-xs-2 col-xs-offset-2">
                        <div className="list-group">
                            {/* 在React中靠路由链接实现切换组件--编写路由链接 */}
                            <NavLink className="list-group-item" to="/about">About</NavLink>
                            <NavLink className="list-group-item" to="/home">Home</NavLink>
                        </div>
                    </div>
                    <div className="col-xs-6">
                        <div className="panel">
                            <div className="panel-body">
                                <Suspense fallback={<Loading/>}>
                                    {/* 注册路由 */}
                                    <Route path="/about" component={About}/>
                                    <Route path="/home" component={Home}/>
                                </Suspense>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}

 

 

 

第二部分 


  • stateHook (useState)

我理解是:条件触发 for state。
 - 如果是 [],表示不监听state。
 - 如果是“未设置”,表示监听所有state。
 
典型应用:断掉链接,卸载组件。(点击按钮,改变状态,触发useEffect的方法完成unmount 操作)
这是传统的方法,使用hook方式后,又会带来什么优势?
//类式组件
class Demo extends React.Component { state = {count:0} myRef = React.createRef() add = ()=>{ this.setState(state => ({count:state.count+1})) } unmount = ()=>{ ReactDOM.unmountComponentAtNode(document.getElementById('root')) // 这是被用户触发;该函数“又”触发” componentWillUnmount() } show = ()=>{ alert(this.myRef.current.value) } componentDidMount(){ this.timer = setInterval(()=>{ this.setState( state => ({count:state.count+1})) },1000) } componentWillUnmount(){ clearInterval(this.timer) } render() { return ( <div> <input type="text" ref={this.myRef}/> <h2>当前求和为{this.state.count}</h2> <button onClick={this.add}>点我+1</button> <button onClick={this.unmount}>卸载组件</button> <button onClick={this.show}>点击提示数据</button> </div> ) } }

如下,一个 useEffect 外加 return,可以替代如上这些功能:组件的后台程序,以及自动卸载。

-- componentDidMount()

-- componentDidUpdate()

-- componentWillUnmount()

function Demo(){
    //console.log('Demo');

    const [count,setCount] = React.useState(0)
    const myRef = React.useRef()

    React.useEffect(()=>{
let timer
= setInterval(()=>{ setCount(count => count+1 ) },1000)
return ()=>{ clearInterval(timer)   // 相当于 componentWillUnmount() }
},[]) 。。。

 

  •  refHook (useRef)

class Demo extends React.Component {

    state = {count:0}
    myInputRef = React.createRef()

    show = ()=>{
        alert(this.myInputRef.current.value)
    }

    render() {
        return (
            <div>
                <input type="text" ref={this.myInputRef}/>
                <h2>当前求和为{this.state.count}</h2>
                <button onClick={this.add}>点我+1</button>
                <button onClick={this.unmount}>卸载组件</button>
                <button onClick={this.show}>点击提示数据</button>
            </div>
        )
    }
}
函数组件,基本一样,只是:1) createRef -> useRef,  2) 不再需要 this。
function Demo(){
    //console.log('Demo');
    const myInputRef = React.useRef()//提示输入的回调
    function show(){
        alert(myInputRef.current.value)
    }
}

 

第三部分 


  • Fragment

是个虚的</div>。

import React, { Component,Fragment } from 'react'

export default class Demo extends Component {
    render() {
        return (
            <Fragment key={1}>
                <input type="text"/>
                <input type="text"/>
            </Fragment>
        )
    }
}

 

  • Context

使用函数组件时,不通过props传参,而是通过更方便的方式。

类组件的弊端,在于provide 会导致每个子类都要写一行“声明代码”。

如下可见,(1) (2) (3) 被添加后,就可以通过(4) 直接从 "爷爷组件" 得到其参数给 "孙子组件"。

//创建Context对象
const MyContext = React.createContext()  // (1)
const {Provider,Consumer} = MyContext    // (2)
export
default class A extends Component { state = {username:'tom',age:18} render() { const {username,age} = this.state return ( <div className="parent"> <h3>我是A组件</h3> <h4>我的用户名是:{username}</h4> <Provider value={{username,age}}> <B/> </Provider> </div> ) } }
class C extends Component {
//声明接收context static contextType = MyContext   // (3) render() { const {username,age} = this.context // (4) return ( <div className="grand"> <h3>我是C组件</h3> <h4>我从A组件接收到的用户名:{username},年龄是{age}</h4> </div> ) } }

如果是“函数孙组件”,则可以考虑 Consumer。当然,也可以考虑Hook的方式,goto: [React] Hook,支持了useContext。

function C(){
    return (
        <div className="grand">
            <h3>我是C组件</h3>
            <h4>我从A组件接收到的用户名:
            <Consumer>
                {value => `${value.username},年龄是${value.age}`}   // 这里替代了(4)的方式,可以直接从value这个key中提取参数值。
            </Consumer>
            </h4>
        </div>
    )
}

 

 

 

 

组件优化 PureComponent


Ref: 123 尚硅谷 react教程 扩展8 PureComponent 

分析

  • Component的2个问题

  1. 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低

  2. 只当前组件重新render(), 就会自动重新render子组件纵使子组件没有用到父组件的任何数据 ==> 效率低

即使是 setState({}) 都会触发render,这不合理,需要优化。 

  

  • 效率高的做法

只有当组件的state或props数据发生改变时才重新render()

 

  • 原因

Component中的 shouldComponentUpdate() 总是返回true

  

解决

办法1:
重写 shouldComponentUpdate()方法
比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
shouldComponentUpdate(nextProps,nextState){
        // console.log(this.props,this.state); //目前的props和state
        // console.log(nextProps,nextState); //接下要变化的目标props,目标state
        return !this.state.carName === nextState.carName
    }
组件中都加入类似的条件判断
办法2: (推荐方法)
使用PureComponent
PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
import React, { PureComponent } from 'react'

export default class Parent extends PureComponent {
}

class Child extends PureComponent {
}

 

注意:
只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
不要直接修改state数据, 而是要产生新数据。
红框:返回的是新对象。杜绝蓝框的写法。

    addStu = ()=>{
        /* const {stus} = this.state
        stus.unshift('小刘')
        this.setState({stus}) */

        const {stus} = this.state
        this.setState({stus:['小刘',...stus]})
    }

 


 

 

高级 部分 


  • renderProps 插槽组件

像常规标签这么去用,写法如下,比较怪异。这是 ChildrenProps.

這招實在太秀了!~~

但,A组件中的数据 如何传递给 B组件?

<A>
    <B/>
</A>

 

解决方案就是 RenderProps 如下:类似Vue 的插槽技术。

定义A

class A extends Component { state
= {name:'tom'} render() { console.log(this.props); const {name} = this.state
return ( <div className="a"> <h3>我是A组件</h3> {this.props.render(name)} </div> ) } }

使用时,可以选择A的子组件。A相当于一个“插槽组件”。

使用A

export default class Parent extends Component { render() { return ( <div className="parent"> <h3>我是Parent组件</h3> <A render={(name)=><C name={name}/>}/> </div> ) } }

 

 如果子组件容易 发生exception,父组件就要准备一些处理代码,把错误限制在一个可控的范围。

[React] 09 - Tutorial: components,有讲生命周期。

import React, { Component } from 'react'
import Child from './Child'

export default class Parent extends Component {

    state = {
        hasError:'' //用于标识子组件是否产生错误
    }

    //当Parent的子组件出现报错时候,会触发getDerivedStateFromError调用,并携带错误信息
    static getDerivedStateFromError(error){
        console.log('@@@',error);
        return {hasError:error}
    }

    componentDidCatch(){
        console.log('此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决');
    }

    render() {
        return (
            <div>
                <h2>我是Parent组件</h2>
                {this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child/>}  # 返回一个error handling web page
            </div>
        )
    }
}

 

posted @ 2018-05-20 20:36  郝壹贰叁  阅读(342)  评论(0编辑  收藏  举报