[React] Hook - extension & optimization
第一部分
-
setState
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)
//类式组件
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> ) } }
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
分析
只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
只当前组件重新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> ) } }
-
ErrorBoundary
如果子组件容易 发生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>
)
}
}

浙公网安备 33010602011771号