react扩展及新特性
setState更新状态的2种写法
(1). setState(stateChange, [callback])------对象式的setState
1.stateChange为状态改变对象(该对象可以体现出状态的更改)
2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
(2). setState(updater, [callback])------函数式的setState
1.updater为返回stateChange对象的函数。
2.updater可以接收到state和props。
4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:
1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
(1).如果新状态不依赖于原状态 ===> 使用对象方式
(2).如果新状态依赖于原状态 ===> 使用函数方式
(3).如果需要在setState()执行后获取最新的状态数据,
要在第二个callback函数中读取
代码示例:
import React, { Component } from 'react'
export default class Demo extends Component {
state = {count:0}
add = ()=>{
/* //1.获取原来的count值
const {count} = this.state
//2.更新状态
this.setState({count:count+1},()=>{
console.log(this.state.count);
})
//函数式的setState
this.setState((state)=>{
return {count:state.count+1}
})
}
render() {
return (
<div>
<h1>当前求和为:{this.state.count}</h1>
<button onClick={this.add}>点我+1</button>
</div>
)
}
}
路由懒加载(路由组件的lazyLoad)
//1.需要引入lazy,Suspense。
//2.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
//3.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
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-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/>}>
{/* 注册路由 需要Suspense 包裹*/}
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
</Suspense>
</div>
</div>
</div>
</div>
</div>
)
}
}
Hooks
useState
import React ,{useState}from "react";
function Child1 () {
const [count,setCount] =useState(0)
let clickHandle=()=>{
// 写法一:
setCount(count+1)
// 写法二:
// setCount((count)=>{
// return count+1
// })
}
return (
<div>
<div>{count}</div>
<button onClick={clickHandle}>点我加1</button>
</div>
)
}
export default Child1
useEffect(模拟生命周期)
componentDidMount vs useEffect
类组件中,我们这样编写componentDidMount:
class Example extends React.Component {
componentDidMount() {
console.log('Did mount!');
}
render() {
return null;
}
}
在函数组件中,我们可以使用useEffect这样编写
import React ,{useEffect}from "react";
function Example() {
// 注意不要省略第二个参数 [],这个参数保证函数只在挂载的时候进行,而不会在更新的时候执行。
// 假如第二个参数为[num],会在初始化和num值改变时执行
useEffect(() => console.log('mounted'), []);
return null;
}
componentDidUpdate vs useEffect
类组件中,我们这样编写componentDidUpdate:
componentDidUpdate() {
console.log('mounted or updated');
}
而在函数组件中,我们使用useEffect起到同样的效果:
useEffect(() => console.log('mounted or updated')); // 不需要指定第二个参数
值得一提的是,现在官方推荐的编程规范就是不区分 update 阶段和 mount 阶段,两个阶段视为一致。
componentWillUnmount vs useEffect
类组件中,我们这样编写componentWillUnmount:
componentWillUnmount() {
console.log('will unmount');
}
而在函数组件中,我们使用useEffect起到同样的效果:
useEffect(() => {
return () => {
console.log('will unmount'); // 直接使用return返回一个函数,这个函数在unmount时执行。
}
}, []);
你也可以使用useEffect 组合componentDidMount 和 componentDidUnmount。
useEffect(()=>{
console.log("mounted");
return () => {
console.log("unmounted");
}
}, [Started]) // 前后两次执行的Started相等时,useEffect代码生效,否则跳过。
useRef
import React,{useRef} from 'react'
import ReactDOM from 'react-dom'
//类式组件
/* class Demo extends React.Component {
myRef = React.createRef()
show = ()=>{
alert(this.myRef.current.value)
}
render() {
return (
<div>
<input type="text" ref={this.myRef}/>
<button onClick={this.show}>点击提示数据</button>
</div>
)
}
} */
//函数组件:
function Demo(){
const myRef = useRef()
//提示输入的回调
function show(){
alert(myRef.current.value)
}
return (
<div>
<input type="text" ref={myRef}/>
<button onClick={show}>点我提示数据</button>
</div>
)
}
export default Demo
Fragment (标签碎片,可以不用必须有一个真实的DOM根标签了,真正渲染时不存在此标签)
<Fragment><Fragment>
<></>
Context 一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
使用
1) 创建Context容器对象:
const XxxContext = React.createContext()
2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
<xxxContext.Provider value={数据}>
子组件
</xxxContext.Provider>
3) 后代组件读取数据:
//第一种方式:仅适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中的value数据
//第二种方式: 函数组件与类组件都可以
<xxxContext.Consumer>
{
value => ( // value就是context中的value数据
要显示的内容
)
}
</xxxContext.Consumer>
代码示例:
import React, { Component } from 'react'
import './index.css'
//创建Context对象
const MyContext = React.createContext()
const {Provider,Consumer} = MyContext
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 B extends Component {
render() {
return (
<div className="child">
<h3>我是B组件</h3>
<C/>
</div>
)
}
}
/* class C extends Component {
//声明接收context
static contextType = MyContext
render() {
const {username,age} = this.context
return (
<div className="grand">
<h3>我是C组件</h3>
<h4>我从A组件接收到的用户名:{username},年龄是{age}</h4>
</div>
)
}
} */
function C(){
return (
<div className="grand">
<h3>我是C组件</h3>
<h4>我从A组件接收到的用户名:
<Consumer>
{value => `${value.username},年龄是${value.age}`}
</Consumer>
</h4>
</div>
)
}