上接
Easy的React学习笔记(一.基础):
https://blog.csdn.net/qq_37279880/article/details/102487420
目录
15_使用PropTypes校验传递值的类型(Typechecking With PropTypes)
16_ref属性提高代码语义化、setState的回调函数与console.log
1.Mounting: 组件挂载阶段(组件挂载即组件插入DOM树)
18_用生命周期函数shouldComponentUpdate改善程序性能
解决方法:用管理员身份运行命令提示符(cmd)再继续安装axios,安装成功:
15_使用PropTypes校验传递值的类型(Typechecking With PropTypes)
官方文档连接:
https://react.docschina.org/docs/typechecking-with-proptypes.html
1.属性类型校验的意义:
在父组件向子组件传递数据时,是在父组件中子组件添加属性(props)的方式,但是在子组件中并没有对这些属性进行校验,就是说父组件传递过来的任何值都不会报错,这在实际开发中是不允许的,在大型项目中如果不进行校验,开发后期业务逻辑会变得非常混乱出现错误,业务逻辑错误不像语法错误非常容易找,所以属性类型校验就非常有必要了。
应用:
在Xiaojiejie.js中为子组件XiaojiejieItem传递通过添加属性的方式传递了四个值,有字符串、数字、方法,在子组件XiaojiejieItem中都是可以通过PropTypes校验传递过来的值的类型。
XiaojiejieItem.js头部引入PropTypes:
import PropTypes from 'prop-types'
// import a from 'prop-types'
注意这里是导入默认组件,所以前面的PropTypes想取名什么都可以,取a都可以,
关键是校验组件.propTypes的propTypes是唯一写法。
2.校验传递值的类型,格式如下:
组件.propTypes={
属性:PropTypes.属性类型,
}
红框内propTypes是唯一写法,绿框内PropTypes在导入'prop-types'默认组件时,名字隋边取,不过为了方便阅读理解,我取PropTypes
XiaojiejieItem.js完整代码如下:
import React, { Component } from 'react';
import PropTypes from 'prop-types'
// import a from 'prop-types'
class XiaojiejieItem extends Component {
constructor(props){
super(props)
this.handleClick=this.handleClick.bind(this)
}
render() {
return (
<li onClick={this.handleClick}>{this.props.content}</li>
)
}
handleClick(){
// 测试能否取得list的索引
// console.log(this.props.index)
// 子组件不能直接修改父组件的数据,TypeError: Cannot assign to read only property 'list' of object '#<Object>'
// this.props.list=[],报错
this.props.deleteItem(this.props.index)
}
}
XiaojiejieItem.propTypes={
// content:a.string,
// index:a.func
// deleteItem:a.func,
content:PropTypes.string,
index:PropTypes.number,
deleteItem:PropTypes.func
}
export default XiaojiejieItem;
效果(没有报错,因为传递的值和我们要求的PropType值是一样):
如果把index的PropType值改成string,console就会报错了:
XiaojiejieItem.propTypes={
// content:a.string,
// index:a.func
// deleteItem:a.func,
content:PropTypes.string,
index:PropTypes.string,
deleteItem:PropTypes.func
}
意思是index属性传过来的number类型,我们自己要求的是string类型,所以报错了。
当传入的 prop 值类型不正确时,JavaScript 控制台将会显示警告。出于性能方面的考虑,propTypes 仅在开发模式下进行检查。
3.必需值校验(isRequired关键字):
在XiaojiejieItem.js中的<li></li>加入代码{this.personname}为你服务:
render() {
return (
<li onClick={this.handleClick}>{this.props.personname}为你服务{this.props.content}</li>
)
}
此时父组件Xiaojiejie.js没有传递personname的值过来,也不会报错:
如果我们在子组件XiaojiejieItem.js下方校验处,为personname的校验加上isRequired关键字,父组件Xiaojiejie不传值就会报错:
XiaojiejieItem.propTypes={
// content:a.string,
// index:a.func
// deleteItem:a.func,
content:PropTypes.string,
index:PropTypes.number,
deleteItem:PropTypes.func,
personname:PropTypes.string.isRequired
}
Warning: Failed prop type: The prop `personname` is marked as required in `XiaojiejieItem`, but its value is `undefined`.
意思是我们在XiaojiejieItem的校验时要求personname属性必须传值的,所以报错了。
我们在为父组件Xiaojiejie引用子组件XiaojiejieItem时,为XiaojiejieItem的personname属性定义值:
<XiaojiejieItem
content={item}
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
personname='石原里美'
/>
报错解决:
4.默认prop值(defaultProps关键字)
我们在为把父组件Xiaojiejie引用子组件XiaojiejieItem时XiaojiejieItem的personname属性删掉,在XiaojiejieItem.js中为personname属性加入默认值'新垣结衣'
XiaojiejieItem.defaultProps={
personname:'新垣结衣'
}
实现效果:
格式:
子组件名.defaultProps={
属性:'属性名'
}
16_ref属性提高代码语义化、setState的回调函数与console.log
1.用为标签加上ref属性,提高代码可读性(语义化):
React开发过程中通常有不少语义化模糊的代码,代码语义化模糊影响开发效率、代码可读性,我们通过ref属性提高代码代码可读性。
之前的inputChange方法中通过inputValue:e.target.value的写法修改文本框的值,这种写法显然不直观。
如何实现:为标签加上ref属性,从而把标签绑定到'this.标签'上,实现代码语义化
为input标签加入ref属性,通过ref属性、箭头函数把input标签绑定到this.input上:
<input
id='addList'
className='input'
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
// 通过ref属性、箭头函数把input标签绑定到this.input上
ref={(input)=>{this.input=input}}
/>
inputValue:e.target.value就修改为:inputValue:this.input.value
inputChange(e){ //绑定响应事件改变文本框inputValue的值
console.log(this);//onChange事件中重新bind了this指向Xiaojiejie,如果没bind就是undefined
console.log(e);//e是合成事件SyntheticEvent
console.log(e.target);//e.target是合成事件的target属性,即整个input标签
console.log(e.target.value);//e.target.value即input标签的value值
// this.state.inputValue=e.target.value;//错误写法:因为没用没用this.setState方法
// console.log(this);//输出undefined,所以要bind方法绑定this
// console.log(this === window);//false
this.setState({ //React中改变组件内部的状态数据的值要用this.setState方法,注意setState的的大小写
// inputValue:e.target.value
inputValue:this.input.value
})
}
效果:
原理:
实际上通过console.log发现,其实e.target就是input标签,所以我们通过为input标签加入ref属性,通过ref属性、箭头函数把input标签绑定到this.input上,从而实现语义化。
注意:
这就使我们的代码变得语义化和优雅的多。但是就我个人的经验来讲,我是不建议用ref这样操作的,因为React的是数据驱动的,所以用ref会出现各种问题。
2.setState的回调函数与console.log
需求:
ref绑定取得ul内服务项(li)的数量。
实现:
①为ul标签加入ref属性,通过ref属性、箭头函数把ul标签绑定到this.ul上:
<ul ref={(ul)=>{this.ul=ul}}>
{
this.state.list.map((item,index)=>{
return(
// 拆分成子组件组件写法
<XiaojiejieItem
content={item}
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
)
})
}
</ul>
②addList方法中通过this.ul.querySelectorAll('li').length输出ul标签内li的长度
addList(){ //为按钮绑定增加服务项事件
this.setState({
list:[...this.state.list,this.state.inputValue], //...这个是ES6的新语法的扩展运算符,名为扩展运算符。作用把list数组进行了分解,形成了新的数组,然后再进行组合。这种写法更简单和直观,所以推荐这种写法。
inputValue:''
})
console.log(this.ul.querySelectorAll('li').length)
}
HTML DOM querySelectorAll() 方法:
https://www.runoob.com/jsref/met-document-queryselectorall.html
效果(发现控制台console的li长度总比实际的少1):
原因react中的setState方法是异步方法,setState方法的执行是需要时间的,addList方法中每次setState没执行完就已经console输出了。
解决方法:
③setState方法提供了回调函数,把console.log输出写进setState方法的回调函数,以保证输出时setState方法已经执行,修改addList方法代码如下:
addList(){ //为按钮绑定增加服务项事件
this.setState({
list:[...this.state.list,this.state.inputValue], //...这个是ES6的新语法的扩展运算符,名为扩展运算符。作用把list数组进行了分解,形成了新的数组,然后再进行组合。这种写法更简单和直观,所以推荐这种写法。
inputValue:''
},
()=>{
console.log(this.ul.querySelectorAll('li').length)
})
}
实现效果(控制台console的li长度与实际相等):
17_组件的生命周期函数
生命周期函数:
定义:生命周期函数指在某一个时刻组件会自动调用执行的函数,用于在组件不同阶段执行自定义功能。
举例:
render()函数,就是一个生命周期函数,它在state(状态值)发生改变时自动执行。这就是一个标准的自动执行函数。
React组件的生命周期图(生命周期的三个阶段,React16.4 version中把Iconstructor()的Initialization初始化阶段放进了挂载阶段,加粗的方法是常用的生命周期方法):
React组件生命周期主要分为三个阶段():
1.Mounting: 挂载阶段(在组件被创建并插入到 DOM 时)
2.Updation: 更新阶段
3.Unmounting: 卸载阶段
1.Mounting: 组件挂载阶段(组件挂载即组件插入DOM树)
Mounting阶段叫挂载阶段,伴随着整个虚拟DOM的生成,它里边有几个小的生命周期函数,调用顺序如下(其中只有constructor、render、componentDidMount是常用的,如上图所示):
①.constructor初始化(常用)
在构造函数constructor()中初始化this.props、this.state。
constructor()不算生命周期函数,是构造函数,它是ES6的基本语法。
虽然它和生命周期函数的性质一样,但不能认为是生命周期函数。
但是你要心里把它当成一个生命周期函数,把它看成React的Initialization阶段,用来定义属性(props)和状态
(state)。
②.componentWillMount(已过时,几乎不用) : 在组件即将被挂载到页面(组件挂载前)的时刻执行。
//在React 16.9中componentWillMount已被弃用,改为UNSAFE_componentWillMount,而且UNSAFE_componentWillMount也准备不用了、是过时的生命周期方法,在新代码中最好不用。
③.render(常用) : 页面state或props发生变化时执行(组件挂载中)。
render应为纯函数,意味着render不应该修改组件的state,每次调用时都返回相同的结果,并且render不会直接与浏览器交互,若
想与浏览器交互,应该在componentDidMount() 或其他生命周期方法中执行你的操作。保持 render() 为纯函数,可以使组件更容
易思考。
④.componentDidMount(常用) : 组件挂载完成时被执行(在组件已经被渲染到 DOM 中后运行)。
加入红框中代码:
效果:
Warning: componentWillMount has been renamed, and is not recommended for use.
查阅资料,在React 16.9中componentWillMount已被弃用,改为UNSAFE_componentWillMount,修改代码:
效果:
生命周期函数的书写顺序与执行顺序无关:
这就是生命周期的顺序。这三个函数书写时是不用按顺序的,你可以随便改动他们的顺序。
注意:
UNSAFE_componentWillMount和componentDidMount这两个生命周期函数,只在页面刷新时执行一次,而render
函数是只要有
state和props变化就会执行,这个初学者一定要注意。
2.Updating: 组件更新阶段
这是React生命周期中比较复杂的一部分,它有两个基本部分组成,一个是props
属性改变,一个是state
状态改变(这个在生命周期的
图片中可以清楚的看到)。
Updating:即组件发生改变时的更新阶段,
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
注意:
下述方法即将过时,在新代码中应该避免使用它们:
①shouldComponentUpdate()函数:会在组件更新之前,自动被执行。比如写入下面的代码:
shouldComponentUpdate(){
console.log('shouldComponentUpdate----组件发生改变(更新)前执行')
return true
}
文本框输入a,效果如下:
shouldComponentUpdate()函数有两个返回值:true(默认值)、false。
返回值为true:state每次发生变化,组件就会重新渲染,大部分情况下应该遵循默认行为。
当props 或 state 发生变化时,shouldComponentUpdate()函数会在渲染前被调用。
首次渲染或使用orceUpdate(0时不会调用该方法。
返回值为false:就算state每次发生变化,这组件业不会进行更新了。 简单点说,但是返回值为false很少用到,而且返回值为false会导致一系列问题,这里不做过多介绍,详见官方文档的解释:
https://react.docschina.org/docs/react-component.html#shouldcomponentupdate
简单来说,返回true,就同意组件更新;返回false,就反对组件更新。
②componentDidUpdate()函数:在组件更新之后执行,它是组件更新的最后一个环节。
componentDidUpdate(){
console.log('componentDidUpdate----组件更新之后执行')
}
3.Unmounting---组件卸载卸载时
在子组件XiaojiejieItem中加入:
componentWillUnmount(){
console.log('child - componentWillUnmount')
}
点击服务列表项,服务列表项被删除后执行componentWillUnmount方法
18_用生命周期函数shouldComponentUpdate改善程序性能
本项目一直存在性能问题,就是子组件XiaojiejieItem频繁且无用的渲染。
通过React Developer Tools可以清楚地看到,不过要首先进行一些设置,即打开组件渲染时高亮显示的(Highlight updates when components render),设置步骤如下:
可以看到在父组件Xiaojieije的文本框中输入时子组件XiaojiejieItem也在渲染:
我们也可以在XiaojiejieItem.js
的render
函数里加入下面的代码,可以更直观的看到这个问题:
render() {
console.log('child--render')
return (
<li onClick={this.handleClick}>{this.props.personname}为你服务:{this.props.content}</li>
)
}
解决方法:
通过shouldComponentUpdate函数阻止子组件XiaojiejieItem无用渲染
shouldComponentUpdate(nextProps,nestState){
if(nextProps.content!==this.props.content){
return true
}else{
return false
}
}
可以看到控制台没用在父组件发生变化时,子组件没有重复输出child--render,即子组件并没有重复渲染。
这就算是完美解决了子组件的渲染性能问题,在react面试中性能优化会是区分一般程序员和有经验程序员的标准之一。
19_axios数据请求
ajax可以远程请求但是写起来太麻烦了,我们用程序的ajax请求框架Axios来实现。
1.axios安装
在项目根目录下,输入:
npm install -save axios
结果报错:
npm ERR! code EPERM
npm ERR! syscall open
npm ERR! path C:\Program Files\nodejs\node_cache\_cacache\tmp\3219faae
npm ERR! errno -4048
解决方法:用管理员身份运行命令提示符(cmd)再继续安装axios,安装成功:
npm install的4种常用方式的区别(附表格对比):
npm install x:
- 会把x模块安装到项目的node-modules目录中
- 不会修改package.json文件(不添加依赖)
npm install x -g:
- 安装模块到全局,不会把x模块安装到项目的node-modules目录中,具体安装到磁盘哪个位置,要看 npm cinfig prefix的位置
- 不会修改package.json文件(不添加依赖)
npm install x -save
- 会把x模块安装到项目的node-modules目录中
- 修改package.json文件的dependencies属性写入x模块的依赖(添加依赖)
- 之后运行npm install - production或者注明NODE_ENV变量值为production时,会自动把x模块安装到项目的node-modules目录中
npm install x -save-dev
- 会把x模块安装到项目的node-modules目录中
- 修改package.json文件的DevDependencies属性写入x模块的依赖(添加依赖)
- 之后运行npm install - production或者注明NODE_ENV变量值为production时,不会自动把x模块安装到项目的node-modules目录中
npm install x | npm install x -g | npm install x -save | npm install x -save-dev | |
会把x模块安装到项目的node-modules目录中 | Y | N | Y | Y |
是否修改package.json文件(添加依赖) | N | N | Y,package.json文件的dependencies属性写入x模块的依赖 | Y,package.json文件的DevDependencies属性写入x模块的依赖 |
之后运行npm install - production或者注明NODE_ENV变量值为production时,是否把x模块安装到项目的node-modules目录中 | Y | N |
在package.json添加依赖的重要性:
程序开源上传到github,自己在开发是用了npm install,没有用npm install x -save-dev与npm install x -save-dev,即没有添加
依赖,比人下载后项目是跑不起来的,所以在公司做实际项目时依赖是必须要写的。
npm install x -save-dev与npm install x -save-dev使用场景主要区别:
npm install x -save在dependencies属性(生产环境)添加依赖,即项目做完时要跑在服务器了,必须把 把包(模块)进行依赖,
-save在开发模式、生产模式用都可以。
npm install x -save-dev在 DevDependencies属性(开发环境)添加依赖,只在程序员进行测试、项目管理时使用。
2.axios请求数据
在Xiaojiejie.js头部引入axios:
import axios from 'axios'
在生命周期函数componentDidMount请求ajax:
componentDidMount(){
axios.post('https://web-api.juejin.im/v3/web/wbbr/bgeda')
.then((res)=>{console.log('数据请求成功'+JSON.stringify(res))})
.catch((error)=>{console.log('数据请求失败'+error)})
}
建议在componentDidMount函数里执行ajax:
①因为在render里执行,会出现很多问题,比如一直循环渲染。
②在componentWillMount里执行,在使用RN时,又会有冲突。
效果:
3.axios从RAP2远端api请求自己mock(模拟)的数据
上一小节,post用的是一个掘金的临时api,这类api不是自己写的api,随时都有不能用的风险。实际开发过程中通常是前后端分离的,所以我们要自己mock(数据),这里我们选择用RAP2来模拟数据。
http://rap2.taobao.org/account/login
点击新建接口后输入接口名称、地址,按提交就可以了。
小坑1:按复制地址的按钮是没用的,一定要点击地址,在弹出页面复制地址才有用:
编辑请求参数和响应内容:
修改componentDidMount生命周期方法,把post改成get,并把地址改成弹出页面复制地址:
componentDidMount(){
axios.get('http://rap2.taobao.org:38080/app/mock/254023/Xiaojiejie')
// .then((res)=>{console.log('数据请求成功'+JSON.stringify(res))})
.then(
(res)=>{
console.log('数据请求成功'+JSON.stringify(res.data))
}
)
.catch((error)=>{console.log('数据请求失败'+error)})
}
效果如下(数据请求成功):
再修改componentDidMount生命周期方法,想让服务列表的数据编程从api 中get过来的mock数据,加入setState方法:
componentDidMount(){
axios.get('http://rap2.taobao.org:38080/app/mock/254023/Xiaojiejie')
// .then((res)=>{console.log('数据请求成功'+JSON.stringify(res))})
.then(
(res)=>{
console.log('数据请求成功'+JSON.stringify(res.data))
this.setState({list:res.data})
}
)
.catch((error)=>{console.log('数据请求失败'+error)})
}
大坑1:报错TypeError: this.state.list.map is not a function:
在componentDidMount生命周期方法加上两句console.log:
componentDidMount(){
axios.get('http://rap2.taobao.org:38080/app/mock/254023/Xiaojiejie')
// .then((res)=>{console.log('数据请求成功'+JSON.stringify(res))})
.then(
(res)=>{
console.log('数据请求成功'+JSON.stringify(res.data))
console.log('数据请求成功'+JSON.stringify(res.data.mockdata))
}
)
.catch((error)=>{console.log('数据请求失败'+error)})
}
效果:
实际上res.data.mockdata才是我们想要的数组,而res.data根本不是个数组,所以之前componentDidMount中的把list设置成了res.data,而res.data不是数组,才会报错this.state.list.map is not a function,因为只有array.map方法必须是“数组.map”
this.setState({list:res.data})
修改this.setState为 this.setState({list:res.data.mockdata}),componentDidMount完整代码如下:
componentDidMount(){
axios.get('http://rap2.taobao.org:38080/app/mock/254023/Xiaojiejie')
// .then((res)=>{console.log('数据请求成功'+JSON.stringify(res))})
.then(
(res)=>{
console.log('数据请求成功'+JSON.stringify(res.data))
console.log('数据请求成功'+JSON.stringify(res.data.mockdata))
this.setState({list:res.data.mockdata})
}
)
.catch((error)=>{console.log('数据请求失败'+error)})
}
效果(mock数据请求成功):
此时组件列表不再是写死的数据,而是从api接口get过来的的数据了。
20_CSS3实现react动画
用CSS3在React中制作一个显示/隐藏的动画特效(注意:这是css3实现的效果,react只是做了业务逻辑)
1.创建Boss组件,代码如下:
import React, { Component } from 'react';
class Boss extends Component {
constructor(props) {
super(props);
this.state = { }
}
render() {
return (
<div>boss级人物:铠爹</div>
<div><button>召唤boss</button></div>
);
}
}
export default Boss;
2.编写业务逻辑
具体要实现的效果:
点击按钮,“boss级人物:铠爹”这行字出现或隐藏。
实现原理:
步骤一:
在changeToshow函数内通过setState方法修改this.state.toshow的值,进而修改铠爹”这行文字的className属性的值,再把changeToshow绑定在按钮的点击事件,这样每次点击按钮就能改变一次className属性值。
步骤二:再为hide、show添加css即可实现“boss级人物:铠爹”这行文字的显示或隐藏。
步骤一:通过按钮使“boss级人物:铠爹”这行文字的className属性在"hide"和"show "间切换。
boss子组件代码如下(代码看不懂可参考备注):
import React, { Component } from 'react';
class Boss extends Component {
constructor(props) {
super(props);
this.state = {
toshow:true
}
this.changeToshow =this.changeToshow.bind(this)
}
render() {
return (
<div>
{/*this.state.toshow值为true时,className为show意思是显示div内的文字;反之隐藏。 */}
<div className={this.state.toshow?'show':'hide'}>boss级人物:铠爹</div>
<div>
<button onClick={this.changeToshow}>召唤boss</button>
</div>
</div>
);
}
// changeToshow用于切换this.state.toshow的值,为true时切换为false,为false时切换为true
changeToshow(){
this.setState({
toshow:this.state.toshow?false:true
})
}
}
export default Boss;
并在Xiaojiejie.js中引入Boss子组件:
import Boss from './Boss'
在Xiaojiejie.js的render最下方,加入Boss子组件:
<Boss />
点击“召唤boss”按钮效果如下(每次按按钮className都会在show和hide间切换):
步骤二:在style.css为hide、show添加css
.show {
opacity:1;
transition:all 1.5s ;
}
.hide {
opacity:0;
transition:all 1.5s ;
}
效果:
注:
21_CSS3的keyframes(关键帧)动画
keyframes比transition的优势在于可以更细化动画效果,以上一节的点击按钮隐藏、显示动画为例,不仅可以设置透明度,还能设置属性
他和transition
比的优势是它可以更加细化的定义动画效果。比如我们设置上节课的按钮隐藏动画,不仅可以设置透明度,还可以设置颜色。
注:
注意: 使用animation属性来控制动画的外观,还使用选择器绑定动画。keyframes(关键帧)要和animation属性搭配使用。
在style.css中加入如下代码:
.show {
animation:show-item 2s ease-in ;
}
.hide {
animation:hide-item 2s ease-in ;
}
@keyframes show-item {
0%{
opacity: 0;
color: #7B68EE;
}
50%{
opacity: 0.5;
color: #FFD700;
}
100%{
opacity: 1;
color: #FF4500;
}
}
@keyframes hide-item {
0%{
opacity: 1;
color: #00FF00;
}
50%{
opacity: 0.5;
color: blueviolet;
}
100%{
opacity: 0;
color: cadetblue;
}
}
效果(className为hide时,铠爹却无法隐藏):
这是因为没为设置forwards属性,它是用来控制停止到最后一帧的。为show、hide加上forwards属性:
.show {
animation:show-item 2s ease-in forwards;
}
.hide {
animation:hide-item 2s ease-in forwards;
}
效果(35_为show、hide加上forwards属性后铠爹隐藏成功):
总结:keyframes也只是能实现一些简单的动画效果,一些复杂的动画最好还是使用别人造好的轮子。
22_react-transition-group动画组件
1.react-transition-group的优势
React有非常好的开发环境,任何开发需要的基本需求都可以找到官方或大神造的轮子,动画也不例外。
react-transition-group动画组件表现很好,可以满足日常动画开发需求,而且是React官方提供的动画过渡库,有完整的API文档。
注:
react-transition-group官方文档
https://reactcommunity.org/react-transition-group/
react-transition-group主要有四个核心库:
2.安装react-transition-group
切换终端为cmd后输入以下代码进行安装(注:):
npm install react-transition-group --save
一开始安装是报错的,然后关闭vscode,之后以管理员身份运行vscode后,安装包成功:
3.使用CSSTransition
引入CSSTransition:
import {CSSTransition} from 'react-transition-group'
使用的方法就和使用自定义组件一样,直接写<CSSTransition>,而且className属性在CSSTransition相对应的是classNames(记得加s)属性。修改上节写的Boss.js文件里的render区域:
render() {
return (
<div>
{/*this.state.toshow值为true时,className为show意思是显示div内的文字;反之隐藏。 */}
{/* <div className={this.state.toshow?'show':'hide'}>boss级人物:铠爹</div> */}
<CSSTransition
in={this.state.toshow}
timeout={2000}
classNames={boss-text}
>
<div>boss级人物:铠爹</div>
</CSSTransition>
<div>
<button onClick={this.changeToshow}>召唤boss</button>
</div>
</div>
);
}
注:
<CSSTransition>标签的属性:
in:in值的变化决定了动画是enter还是exit,in值的变化是触发动画的扳机。
timeout:动画持续时间(ms)
classNames:css类名
修改style.css:
.boss-text-enter {
opacity: 0;
}
.boss-text-enter-active {
opacity: 1;
transition: opacity 2000ms;
}
.boss-text-enter-done {
opacity: 1;
}
.boss-text-exit {
opacity: 1;
}
.boss-text-exit-active {
opacity: 0;
transition: opacitty 2000ms;
}
.boss-text-exit-done {
opacity: 0;
}
报错:
原因是classNames={boss-text}不应该是{},而是引号
修改代码:
<CSSTransition
in={this.state.toshow}
timeout={2000}
classNames='boss-text'
>
效果:
注:
xxx-enter: 进入(入场)前的CSS样式;
xxx-enter-active:进入动画直到完成时之前的CSS样式;
xxx-enter-done:进入完成时保留的CSS样式;
xxx-exit:退出(出场)前的CSS样式;
xxx-exit-active:退出动画知道完成时之前的的CSS样式。
xxx-exit-done:退出完成时保留的CSS样式。
unmountOnExit属性:在元素退场时,自动把DOM也删除,这是以前用CSS动画没办法做到的。
元素退场时,把 <div>boss级人物:铠爹</div> 也删除了
4.使用transitionGrop
需求:为服务列表添加动画
实现:
在Xiaojiejie.js的头部引入transitionGrop:
import {TransitionGroup,CSStransition} from 'react-transition-group'
修改Xiaojiejie.js,为ul内,最外层加入TransitionGroup标签,为子组件XiaojiejieItem外层包裹CSSTransition标签,代码如下:
<ul ref={(ul)=>{this.ul=ul}}>
<TransitionGroup>
{
this.state.list.map((item,index)=>{
return(
<CSSTransition
// in={this.state.toshow}
timeout={2000}
classNames='boss-text'
unmountOnExit
appear={true}
key={item+index}
>
<XiaojiejieItem
content={item}
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
</CSSTransition>
)
})
}
</TransitionGroup>
</ul>
CSSTransition的appear属性:
appear={true},意思是想让组件出现时就有动画效果。
实现效果(服务列表添加动画成功):