1、状态更新status
(1)status的初始化
status的初始化需要在构造方法里面,使用=赋值一个对象进行初始化数据。
import React from "react";
class App1 extends React.Component {
constructor(props) {
super(props);
this.state = {
age: 15
}
}
}
export default App1
(2)status的更新状态
status更新数据使用setStatus方法,可以传递一个对象和一个函数。
- 可以接收一个函数,这个函数接受两个参数,第一个参数表示上一个状态值,第二参数表示当前的 props;
this.setState({
age: 2,
});
- 也可以接受一个对象
/**
*
* 接收一个参数,该参数表示设置数据之前的参数,变更之前的参数
*/
this.setState(prevState => ({
age:20
}));
/**
* 接收两个参数,prevState表示变化前的数据,props表示变化后的数据
*/
this.setState((prevState, props) => {
return {
age: prevState.age + 1
};
});
this.setState((prevState, props) => ({
age: prevState.age + 1
}));
2、生命周期
- 直接加载:constructor -> componentWillMount -> componentDidMount
- 更新status:componentWillUpdate -> shouldComponentUpdate
componentWillMount:组件即将被挂载(渲染到页面)的时候执行,只执行一次
componentDidMount:组件被挂载之后(页面渲染完成)的时候执行,只执行一次
shouldComponentUpdate返回值:组件是不是要被更新?
- false:不要更新
- true:要更新,一般是true
componentWillUpdate:当组件将更新时候执行,shouldComponentUpdate返回true时候被执行,false这不会被执行
componentDidUpdate:组件更新完毕之后执行
componentWillReceiveProps:前提:从父组件接收参数。当父组件的render函数被执行了,子组件的这个生命周期函数会被执行。当前组件没有prop传值的时候不会被执行
import React from "react";
/**
* 生命周期
*/
class App2 extends React.Component {
constructor(props) {
console.log('constructor')
super(props);
this.state = {
num: 1
}
}
/**
* 此处发起请求会引起主线程阻塞
*/
componentWillMount() {
console.log('componentWillMount')
}
/**
* 网上流传此处发起请求会引起二次render
*
* 官方推进实际从这个地方进行忘了请求
*/
componentDidMount() {
console.log('componentDidMount')
}
/**
* 将要更新
* @param nextProps
* @param nextState
* @param nextContext
*/
componentWillUpdate(nextProps, nextState, nextContext) {
}
/**
* 更改数据后会触发
*
* false:更新后不会回调其他函数
* true:更新后会回调其他函数
*/
shouldComponentUpdate(nextProps, nextState, nextContext) {
console.log('shouldComponentUpdate')
return false;
}
changeHandler(e) {
this.setState({
num: e.target.value
})
}
/**
* 渲染的过滤
* @returns {*}
*/
render() {
console.log('render')
return (
<div>
我是是数字:{this.state.num}
<hr/>
<input type={'text'} value={this.state.num} onChange={event => {
this.changeHandler(event)
}}/>
</div>
);
}
}
export default App2
3、传值props
- 传递变量
- 传递dom
- 传递方法
(1)父——>子
import React from "react";
/**
* proper传值
* 1、传递变量
* 2、传递dom
*
* this.props.children 可能存在3种情况:对象、数组、undefined
*/
class App3 extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Son name={'张三'} age={'66'}>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</Son>
);
}
}
class Son extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<p>Name: {this.props.name}</p>
<p>age: {this.props.age}</p>
<p>child:{this.props.children}</p>
</div>
);
}
}
export default App3
(2)子——>父
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
aChildMag: ""
}
this.getChildMag = this.getChildMag.bind(this);
}
render() {
return (
<div>
<A msg = {this.getChildMag} />
<h3>A子组件传来的值为:{this.state.aChildMag}</h3>
</div>
);
}
getChildMag(mag) {
this.setState({
aChildMag: mag
})
}
}
class A extends React.Component {
render() {
return (
<button onClick={()=>{this.props.msg('AAA-哈哈哈')}}>点击</button>
);
}
}
export default App;
(3)子——>子
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
aChildMag: ""
}
this.getChildMag = this.getChildMag.bind(this);
}
render() {
return (
<div>
<A msg = {this.getChildMag} />
<h3>A子组件传来的值为:{this.state.aChildMag}</h3>
<B msg = {this.state.aChildMag} />
</div>
);
}
getChildMag(mag) {
this.setState({
aChildMag: mag
})
}
}
class A extends React.Component {
render() {
return (
<button onClick={()=>{this.props.msg('AAA-哈哈哈')}}>点击</button>
);
}
}
class B extends React.Component {
render() {
return (
<h3>这行数据是子组件A传递过来的:{this.props.msg}</h3>
);
}
}
export default App;
4、属性的约定和传值
在子类中约定数据类型,父类传递数据的时候必须传递该数据类型,否则发出警告,详细如下:
import React from "react";
import PropTypes from "prop-types"
class TodoItem extends React.Component {
constructor(props) {
super(props);
this.handleItemDelete = this.handleItemDelete.bind(this)
}
render() {
const {text, itemVlue} = this.props;
return (
<React.Fragment>
<div onClick={event => this.handleItemDelete()}>
{text} - {itemVlue}
</div>
</React.Fragment>
);
}
/**
* 子组件如何修改父组件的内容?
*/
handleItemDelete() {
const {itemDelete, itemIndex} = this.props;
itemDelete(itemIndex)
}
}
/**
* 强制类型校验
* @type {{itemDelete: Requireable<(...args: any[]) => any>, itemIndex: Requireable<number>, content: Requireable<string>}}
*/
TodoItem.propTypes = {
//content: PropTypes.string,
content: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), //既可以是number又可以是String:
itemDelete: PropTypes.func,
itemIndex: PropTypes.number, // 更改:itemIndex: PropTypes.number为itemIndex: PropTypes.string类型不一致,会抛出警告1
text: PropTypes.string.isRequired //text没有传递,不报错,应为发现没有传递并不会去检测,如果加上isRequired,则有警告2,这时候需要定义默认值
}
//1、Warning: Failed prop type: Invalid prop `itemIndex` of type `number` supplied to `TodoItem`, expected `string`
//2、Warning: Failed prop type: The prop `text` is marked as required in `TodoItem`, but its value is `undefined`.
/**
* 默认属性:对于传递的值没有传递则设置默认值,设置默认值,同事如果有强制类型校验isRequired,则不会发出警告,例如Text
*/
TodoItem.defaultProps = {
text: '我是默认数据'
}
export default TodoItem
为了防止多余的div嵌套出现多层:
- 在React16以上版本使用return ([])代码return()
- 兼容低版本则使用return <React.Fragment></React.Fragment>包裹
6、路由匹配
- <NavLink to={'/man'} activeStyle={{color: 'red'}}>男样式</NavLink>
- <Route path={'/'} exact component={Home}></Route>
- <Redirect to={'/'}/>
- exact:表示精确匹配,首页/使用,因为/太多,会匹配太多东西 ;其他页面不需要使用
- Switch:横向匹配,只匹配置一个。不设置的话像/man,/ma这样就会出现异常
- Drdirect:用于都没有匹配上设置404,配合Switch放在最后一行。to显示默认地址
- NavLink:页面跳转,Navlink是link标签的升级版本,内部被渲染成a标签
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect, NavLink
} from "react-router-dom";
/**
*
* 问了防止多余的div嵌套出现多层,
* 1、在React16以上版本使用return ([])代码return()
* 2、兼容低版本则使用return <React.Fragment></React.Fragment>包裹
*
* exact:表示精确匹配,首页/使用,因为/太多,会匹配太多东西 ;其他页面不需要使用
* Switch:横向匹配,只匹配置一个。不设置的话像/man,/ma这样就会出现异常
* Drdirect:用于都没有匹配上设置404,配合Switch放在最后一行。to显示默认地址
*
* @returns {*}
*/
class App5 extends React.Component {
render() {
return ([
<div>
<h3>头部</h3>
<Router>
{/*Navlink是link标签的升级版本,内部被渲染成a标签*/}
<NavLink to={'/man'} activeStyle={{color: 'red'}}>男样式</NavLink>
<NavLink to={'/ma'} activeClassName='selected'>女样式</NavLink>
<Route path={'/'} exact component={Home}></Route>
<Route path={'/a'} component={Footer}></Route>
<Switch>
<Route path={'/ma'} component={Ma}></Route>
<Route path={'/man'} component={Man}></Route>
</Switch>
<Redirect to={'/'}/>
</Router>
<h3>底部</h3>
</div>
])
}
}
class Home extends React.Component {
render() {
return (
<h1>
我是首页
</h1>
);
}
}
class Footer extends React.Component {
render() {
return (
<h1>
我是Footer
</h1>
);
}
}
class Man extends React.Component {
render() {
return (
<h1>
我是Man
</h1>
);
}
}
class Ma extends React.Component {
render() {
return (
<h1>
我是Ma
</h1>
);
}
}
export default App5
7、路由传参
字符串的传递方式:
<Route path={'/article/:id'} component={Article}/>
访问:http://localhost:3000/article/6
console.log(this.props.match.params.id)
可以拿到id
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect, NavLink
} from "react-router-dom";
class App6 extends React.Component {
render() {
return (
<div>
<h1>头部</h1>
<Router>
{/*字符串的传递方式*/}
<NavLink to={'/article/5'}>去article</NavLink>
<Route path={'/article/:id'} component={Article}/>
</Router>/
<h1>底部</h1>
</div>
);
}
}
class Article extends React.Component {
componentWillMount() {
//这块能拿到数据
console.log(this.props.match.params.id)
}
render() {
return (
<div>
<h1>文章组件</h1>
</div>
);
}
}
export default App6
8、子组件调用父组件的方法
/**
* 父组件的handleItemDelete方法传递到子类
**/
getTodoList() {
return (
this.state.list.map((value, index) => {
return <Item
key={index}
itemVlue={value}
itemIndex={index}
itemDelete={this.handleItemDelete.bind(this)}>
{/*这块需要强制绑定为父组件的this,否则在子组件中找不到*/}
</Item>
})
);
}
handleItemDelete(index) {
//注意,这里不能直接修改status的list,不符合规范
const list = [...this.state.list]
list.splice(index, 1)
this.setState({
list: list
})
}
class Item extends React.Component {
constructor(props) {
super(props);
this.handleItemDelete = this.handleItemDelete.bind(this)
}
render() {
return (
<React.Fragment>
<div onClick={event => this.handleItemDelete()}>
{this.props.itemVlue}
</div>
</React.Fragment>
);
}
/**
* 子组件如何修改父组件的内容?
*/
handleItemDelete() {
this.props.itemDelete(this.props.itemIndex)
}
}
export default Item
9、React动画
import React, {Fragment, Component} from 'react';
import './style.css'
class App extends Component {
constructor(prop) {
super(prop);
this.state = {
isShow: true
}
this.handleToggle = this.handleToggle.bind(this)
}
render() {
return (
<Fragment>
<div className={this.state.isShow ? 'show' : 'hide'}>React动画</div>
<CSSTransition
in={this.state.isShow}
timeout={1000}
classNames="fade"
unmountOnExit
onEnter={(el) => el.style.color='blue'}
appear={true}
>
<div>CSSTransition动画</div>
</CSSTransition>
<button onClick={event => this.handleToggle()}>toggle</button>
</Fragment>
);
}
handleToggle() {
this.setState({
isShow: this.state.isShow ? false : true
})
}
}
export default App;
style.css
.show {
opacity: 1;
transition: all 1s ease-in;
}
.hide {
animation: hide-item 2s ease-in forwards;
}
第三方框架:react-transition-group
10、this指向问题
- 普通函数中this的指向,是this执行时的上下文
- 箭头函数中this的指向,是this定义时的上下文
11、模块化使用
index.css如果多个模块都有index.css,还存在index.css都有title这个className这种情况css就冲突了,因为最终都会放在App.js中执行,例如hellow/index.css和title/index.css都有:
.title {
background: red;
}
有两种方式:
(1)使用less-->使用场景多
.hello {
.title {
background: #61dafb;
}
}
(2)在index.css中间加上模块名称,例如index.module.css,这里的module不能改成其他的--->使用场景不太多
hellow/index.js使用如下:
import hello from './index.module.css'
export default function Hello(){
return <div className={hello.title}>hello</div>
}
12、快捷键使用-westrom
- rcc:创建一个类组件
- rsf:创建一个函数组件
在settings -> Editor -> Live Templates可以修改或者添加模板样式。