文章目录
1、state
1、state定位类似于vue组件中的data,但是使用和更新不太方便,更新模式类似于vuex中的state更新
2、setState为异步更新,需要获取更新后的值应该在setState第二个参数中执行,第二个参数为回调函数
3、setState有两种更新方式,第一种是传入对象,第二种为使用函数,函数可以接收preState和props作为参数,因此,在需要根据之前的值更新的场景中使用较多
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
</head>
<body>
<div id="test"></div>
<div id="test1"></div>
<script type="text/babel">
//总结
// 1、state定位类似于vue组件中的data,但是使用和更新不太方便,更新模式类似于vuex中的state更新
// 2、setState为异步更新,需要获取更新后的值应该在setState第二个参数中执行,第二个参数为回调函数
// 3、setState有两种更新方式,第一种是传入对象,第二种为使用函数,函数可以接收preState和props作为参数,
// 因此,在需要根据之前的值更新的场景中使用较多
//state完整写法
class Mytest extends React.Component{
constructor(props){
super(props)
this.state={
status : 'z1'
}
this.changeStatus = this.changeStatus.bind(this)
}
changeStatus(){
//1、对象方式,
// this.setState({
// status:'z2'
// },()=>console.log(this.state.status))
// //在此处打印出的为z1,回调函数中打印的为最新值z2
// console.log(this.state.status)
//2、函数方式,
this.setState(this.updateState,()=>console.log(this.state.status))
//在此处打印出的为z1,回调函数中打印的为最新值z2
console.log(this.state.status)
}
updateState(preState,props){
return {status:preState.status+'zzzzzz'}
}
render(){
return <div><button onClick={this.changeStatus}>测试</button> <span>{this.state.status}</span></div>
}
}
//state简写
// class Mytest extends React.Component{
// state={
// status : 'zzz'
// }
// changeStatus=()=>{
// this.setState({
// status:'czz'
// })
// }
// render(){
// return <div><button onClick={this.changeStatus}>测试</button> <span>{this.state.status}</span></div>
// }
// }
ReactDOM.render(<Mytest/>,document.getElementById('test'))
</script>
</body>
</html>
2、props
个人总结
1、个人感觉比vue中prop方便的地方在于如果没有额外的限定,可以不需定义就直接使用prop
2、不方便的地方在于需要看代码都使用了哪些prop,无法在组件上方不需关注逻辑代码的地方直接看到该组件都使用了哪些props
3、如果不使用解构赋值,则每次使用都需要this.props.xxx,调用麻烦
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<script src="./js/prop-types.js"></script>
</head>
<body>
<div id="test"></div>
<script type="text/babel">
class Mytest extends React.Component {
constructor(props) {
super(props)
}
static propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number
}
static defaultProps = {
age: 18
}
render() {
const { name, age } = this.props
return (
<div>
<ul>
<li>{name}</li>
<li>{age}</li>
</ul>
</div>
)
}
}
const person = {
name: 'z1',
age: 12
}
//单个传入
ReactDOM.render(<Mytest name={person.name} age={person.age} />, document.getElementById('test'))
// 一起传入
// ReactDOM.render(<Mytest {...person} />, document.getElementById('test'))
</script>
</body>
</html>
3、ref
1、ref用于获取节点,vue中有相同概念,vue中的ref类似于React中的字符串ref
2、字符串ref性能存在问题,后续版本可能废除
3、回调类型的ref在使用内联函数时会存在更新时调用两遍第一遍获取不到参数的问题
4、createRef为最推荐的写法//state完整写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
</head>
<body>
<div id="test"></div>
<script type="text/babel">
// 1、字符串类型
// class Mytest extends React.Component{
// constructor(props){
// super(props)
// this.state={
// status : 'zzz'
// }
// this.changeStatus = this.changeStatus.bind(this)
// }
// changeStatus(){
// this.setState({
// status:this.state.status+'z'
// })
// console.log(this.refs.btn1)
// }
// render(){
// return <div><button onClick={this.changeStatus} ref='btn1'>测试</button> <span>{this.state.status}</span></div>
// }
// }
// 2、回调函数
// class Mytest extends React.Component{
// constructor(props){
// super(props)
// this.state={
// status : 'zzz'
// }
// this.changeStatus = this.changeStatus.bind(this)
// }
// changeStatus(){
// this.setState({
// status:this.state.status+'z'
// })
// console.log(this.btn1)
// }
// setBtn1 = (e)=>{
// this.btn1 = e
// }
// render(){
// // return <div><button onClick={this.changeStatus} ref={(e)=>{this.btn1=e}}>测试</button> <span>{this.state.status}</span></div>
// return <div><button onClick={this.changeStatus} ref={this.setBtn1}>测试</button> <span>{this.state.status}</span></div>
// }
// }
// 3、createRef
class Mytest extends React.Component{
constructor(props){
super(props)
this.state={
status : 'zzz'
}
this.changeStatus = this.changeStatus.bind(this)
this.btn1 = React.createRef()
}
changeStatus(){
this.setState({
status:this.state.status+'z'
})
console.log(this.btn1.current)
}
render(){
return <div><button onClick={this.changeStatus} ref={this.btn1}>测试</button> <span>{this.state.status}</span></div>
}
}
ReactDOM.render(<Mytest />, document.getElementById('test'))
</script>
</body>
</html>
4、组件生命周期
4.1 旧生命周期
总结
1、对于单个组件来说,在挂载阶段,执行生命周期的顺序为constructor,componentWillMount,render,componentDidMount;在更新阶段,执行shouldComponentUpdate,componentWillUpdate,render,componentDidUpdate;在卸载阶段,执行componentWillUnmount
2、对于父子组件来说,与vue大体类似,但vue卸载时先卸载子组件再卸载父组件
在挂载阶段,先调用父组件的constructor,componentWillMount,render,再调用子组件的constructor,componentWillMount,render;在更新阶段,执行父组件shouldComponentUpdate,componentWillUpdate,render,再调用子组件componentWillReceiveProps(首次绑定不触发); shouldComponentUpdate,componentWillUpdate,render,最后调用componentDidUpdate
在卸载阶段,先执行父组件componentWillUnmount,再执行子组件的componentWillUnmount
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
</head>
<body>
<div id="test"></div>
<div id="test1"></div>
<script type="text/babel">
class Mytest extends React.Component {
constructor(props) {
super(props)
this.state = {
status: 'zzz'
}
this.changeStatus = this.changeStatus.bind(this)
console.log('constructor')
}
changeStatus() {
this.setState({
status: 'czz'
})
}
unMount() {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
render() {
console.log('render')
return (
<div>
{//<button onClick={this.changeStatus}>测试</button>
//<button onClick={this.unMount}>卸载</button>
}
<span>{this.state.status}</span>
<span>{this.props.name}</span>
</div>
)
}
componentWillMount() {
console.log('componentWillMount')
}
componentDidMount() {
console.log('componentDidMount')
}
shouldComponentUpdate() {
console.log('shouldComponentUpdate')
return true
}
componentWillReceiveProps(props){
console.log('componentWillReceiveProps',props)
}
componentWillUpdate() {
console.log('componentWillUpdate')
}
componentDidUpdate() {
console.log('componentDidUpdate')
}
componentWillUnmount() {
console.log('componentWillUnmount')
}
}
class MyTestParent extends React.Component{
constructor(props) {
super(props)
this.state = {
status: 'parent1'
}
this.changeStatus = this.changeStatus.bind(this)
console.log('parent constructor')
}
changeStatus() {
this.setState({
status: 'parent2'
})
}
unMount() {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
render() {
console.log('parent render')
return (
<div>
<button onClick={this.changeStatus}>测试</button>
<button onClick={this.unMount}>卸载</button>
<span>{this.state.status}</span>
<Mytest name = {this.state.status} />
</div>
)
}
componentWillMount() {
console.log('parent componentWillMount')
}
componentDidMount() {
console.log('parent componentDidMount')
}
shouldComponentUpdate() {
console.log('parent shouldComponentUpdate')
return true
}
componentWillUpdate() {
console.log('parent componentWillUpdate')
}
componentDidUpdate() {
console.log('parent componentDidUpdate')
}
componentWillUnmount() {
console.log('parent componentWillUnmount')
}
}
ReactDOM.render(<MyTestParent />, document.getElementById('test'))
</script>
</body>
</html>
执行结果如下
4.2 新生命周期
1、去除用处不多的componentWillMount,componentWillUpdate,componentWillReceiveProps
2、增加了getDerivedStateFromProps(static),必须写成静态方法
3、增加了getSnapshotBeforeUpdate获取更新之前的state,prop等参数经过计算后将需要保留的结果传递到componentDidUpdate,比如需要保存进度条等场景
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./js/17.0.1/react.development.js"></script>
<script src="./js/17.0.1/react-dom.development.js"></script>
<script src="./js/17.0.1/babel.min.js"></script>
</head>
<body>
<div id="test"></div>
<div id="test1"></div>
<script type="text/babel">
class Mytest extends React.Component {
constructor(props) {
super(props)
this.state = {
status: 'zzz'
}
this.changeStatus = this.changeStatus.bind(this)
console.log('constructor')
}
changeStatus() {
this.setState({
status: 'czz'
})
}
unMount() {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
render() {
console.log('render')
return (
<div>
{//<button onClick={this.changeStatus}>测试</button>
//<button onClick={this.unMount}>卸载</button>
}
<p>{this.state.status}</p>
<p>{this.state.status1}</p>
<p>{this.props.name}</p>
</div>
)
}
componentDidMount() {
console.log('componentDidMount')
}
shouldComponentUpdate() {
console.log('shouldComponentUpdate')
return true
}
//用来代替componentWillUpdateProp,,返回根据prop计算出的state属性
static getDerivedStateFromProps(props, state) {
console.log(props, state)
return { status1: props.name + 'getDerivedStateFromProps' }
}
getSnapshotBeforeUpdate() {
console.log('getSnapshotBeforeUpdate');
return 'atguigu'
}
componentDidUpdate() {
console.log('componentDidUpdate')
}
componentWillUnmount() {
console.log('componentWillUnmount')
}
}
class MyTestParent extends React.Component {
constructor(props) {
super(props)
this.state = {
status: 'parent1'
}
this.changeStatus = this.changeStatus.bind(this)
console.log('parent constructor')
}
changeStatus() {
this.setState({
status: 'parent2'
})
}
unMount() {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
render() {
console.log('parent render')
return (
<div>
<button onClick={this.changeStatus}>测试</button>
<button onClick={this.unMount}>卸载</button>
<span>{this.state.status}</span>
<Mytest name={this.state.status} />
</div>
)
}
componentDidMount() {
console.log('parent componentDidMount')
}
shouldComponentUpdate() {
console.log('parent shouldComponentUpdate')
return true
}
//获取更新之前的state,prop等参数经过计算后将需要保留的结果传递到componentDidUpdate
getSnapshotBeforeUpdate() {
console.log('parent getSnapshotBeforeUpdate');
return 'atguigu'
}
componentDidUpdate(state,prop,a) {
console.log(a)
console.log('parent componentDidUpdate')
}
componentWillUnmount() {
console.log('parent componentWillUnmount')
}
}
ReactDOM.render(<MyTestParent />, document.getElementById('test'))
</script>
</body>
</html>
5、路由(react-router-dom)
5.1 一级路由
//引入所需要的的路由组件
//Link中的to与Route中的path一一匹配,注意,必须写在<BroswerRouter或HashRouter组件内部>;Redirect在无匹配路径时重定向到指定路径;Switch可以在匹配到路由后不继续往下匹配,提高效率
import { Route, Redirect, Switch,Link } from 'react-router-dom';
<MyNavLink to='/about'>About</MyNavLink>
<MyNavLink to='/home'>Home</MyNavLink>
<Switch>
<Route path='/home' component={Home} />
<Route path='/about' component={About} />
<Redirect to='/home'></Redirect>
/Switch>
5.2 嵌套路由
//嵌套路由需加上父路由
<div>
<MyNavLink to='/about/message'>message</MyNavLink>
<MyNavLink to='/about/personCenter'>personCenter</MyNavLink>
</div>
<div>
<Route path='/about/message' component={Message}></Route>
<Route path='/about/personCenter' component={PersonCenter}></Route>
</div>
5.3 路由传参
//共三种传参方式,params,search,state
<div>
<ul>
{userInfoList.map(_ => {
return (
<li key={_.id}>
{/* 1、params传参 */}
{/* <MyNavLink to={`/about/personCenter/detail/${_.name}/${_.age}`}>{_.name}</MyNavLink> */}
{/* 2、search传参 */}
{/* <MyNavLink to={`/about/personCenter/detail?name=${_.name}&age=${_.age}`}>{_.name}</MyNavLink> */}
{/* 3、state传参 */}
<MyNavLink to={{pathname:'/about/personCenter/detail',state:{..._}}}>{_.name}</MyNavLink>
</li>
)
})}
</ul>
{/* 1、params传参接收方式 */}
{/* <Route path='/about/personCenter/detail/:name/:age' component={Detail}></Route> */}
{/* 2、3、search和state无需接收 */}
<Route path='/about/personCenter/detail' component={Detail}></Route>
</div>
//参数使用
//1、params传参保存在match.params
// const {name,age} = this.props.match.params
//2、search传参保存在location.search中,可以用querystring解析,或URLSearchParams解析
// (1)querystring解析
// const queryStr = this.props.location.search.substring(1)
// const {name,age} = qs.parse(queryStr)
// (2)URLSearchParams解析
// let params = new URLSearchParams(this.props.location.search.substring(1));
// let name = params.get("name"); // is the string "Jonathan"
// let age = params.get("age"); // is the string "Jonathan"
// 3、state传参
const {name,age} = this.props.location.state
return (
<div>
<ul>
<li>name:{name}</li>
<li>age:{age}</li>
</ul>
</div>
)
路由组件接收到props如下
5.4 编程式路由导航
<Button type='primary' onClick={this.goForward}>前进</Button>
<Button type='primary' onClick={this.return}>后退</Button>
<Button type='primary' onClick={this.push}>push去about</Button>
<Button type='primary' onClick={this.replace}>replace去about</Button>
//go方法可以传入数字,replace覆盖之前历史记录,不产生新的记录
goForward = () => {
this.props.history.goForward()
}
return = () => {
this.props.history.goBack()
}
push = ()=>{
this.props.history.push(
{
pathname:'/about'
}
)
}
replace = ()=>{
this.props.history.replace(
{
pathname:'/about'
}
)
}
5.5 HashRouter和BrowserRouter
1、底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。
2、path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test
3、刷新后对路由state参数的影响
(1) BrowserRouter没有任何影响,因为state保存在history对象中。
(2) HashRouter刷新后会导致路由state参数的丢失!!!
备注:HashRouter可以用于解决一些路径错误相关的问题。
5.6路由懒加载
通过lazy可以实现路由懒加载,注意,使用路由懒加载时需使用来确定在加载过程中的展示内容,否则会报错
// import './App.css';
import React, { Component, lazy, Suspense } from 'react'
import { Route, Redirect, Switch } from 'react-router-dom';
import 'antd/dist/antd.css'
import { Layout } from 'antd'
import MyNavLink from '../components/myNavLink/myNavLink';
import HeaderContent from '../components/Header.jsx'
const Home = lazy(() => import('./Home'))
const About = lazy(() => import('./About'))
const { Header, Content, Footer, Sider } = Layout
class App extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
<Layout style={{ height: '100vh' }}>
<Header style={{ background: 'wheat' }}>
<HeaderContent></HeaderContent>
</Header>
<Layout>
<Sider style={{ background: 'antiquewhite' }}>
<MyNavLink to='/home'>Home</MyNavLink>
<MyNavLink to='/about'>About</MyNavLink>
</Sider>
<Content>
<Switch>
<Suspense fallback={<div>加载中</div>}>
<Route path='/home' component={Home} />
<Route path='/about' component={About} />
</Suspense>
{/* <Redirect to='/home'></Redirect> */}
</Switch>
</Content>
</Layout>
<Footer>Footer</Footer>
</Layout>
</div>
)
}
}
export default App;
6、redux
6.1、用于全局状态管理,类似于vue框架中的vuex,核心概念
store:项目只能有一个store,createStore(reducer)
action:其实是一个普通对象,包含类型和数值
reducer:进行各种计算处理
6.2、普通使用
//index.js,需要手动监听store中的变化,不然页面渲染无法更新
// 监测redux中状态的改变,如redux的状态发生了改变,那么重新渲染App组件
store.subscribe(()=>{
ReactDOM.render(<App/>,document.getElementById('root'))
})
//redux/index.js
import {createStore,applyMiddleware} from 'redux'
import countReducer from './countReducer.js'
import thunk from 'redux-thunk'//使用异步操作时需要使用中间件
export default createStore(countReducer,applyMiddleware(thunk))
//redux/countreducer.js
import {INCREMENT,DECREMENT} from './constant.js'
const initalState = 0
export default function (preState = initalState, action) {
const { type, data } = action
switch (type) {
case INCREMENT:
return preState + data
case DECREMENT:
return preState - data
default:
return preState
}
}
//redux/countAction.js
import {INCREMENT,DECREMENT} from './constant.js'
export const incrementAction = data=>{return {type:INCREMENT,data}}
export const decrementAction = data=>{return {type:DECREMENT,data}}
export const asyncIncrementAction = data=>{
return dispach=>{
setTimeout(()=>{dispach(incrementAction(data))},3000)
}
}
//redux/constant.js
const DECREMENT = 'decrease'
const INCREMENT = 'increase'
export {DECREMENT,INCREMENT}
//components/count.jsx
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { Button, InputNumber } from 'antd'
import store from '../redux/index'
import { incrementAction, decrementAction ,asyncIncrementAction} from '../redux/countAction'
class Count extends Component {
increment=()=>{
let {value} = this.InputNumber
store.dispatch(incrementAction(value*1))
}
decrement=()=>{
let {value} = this.InputNumber
store.dispatch(decrementAction(value*1))
}
asyncIncrement=()=>{
let {value} = this.InputNumber
store.dispatch(asyncIncrementAction(value*1))
}
render() {
return (
<div>
<span>当前结果为:{store.getState()}</span>
<div>
<Button onClick={this.increment}>+</Button>
<Button onClick={this.decrement}>-</Button>
<Button onClick={this.asyncIncrement}>异步加</Button>
</div>
<InputNumber ref={c=>this.InputNumber = c}></InputNumber>
</div>
);
}
}
export default Count;
6.3、react-redux插件
将组件分为UI组件和container组件,UI组件只负责渲染,不能直接调用redux中的api,而是通过props组件调用。container组件对store和dispatch进行映射
//index.js,无需手动监听store变化
import React from 'react'
import ReactDOM from 'react-dom'
import store from './redux/index'
import App from './App'
import { Provider } from 'react-redux'
ReactDOM.render((<Provider store ={store}><App /></Provider>), document.getElementById('root'))
//containers/count.jsx
import CountUI from '../components/CountUI'
import {connect} from 'react-redux'
import { incrementAction, decrementAction ,asyncIncrementAction} from '../redux/countAction'
//1、完整写法
function mapStateToProps(state){
return {count:state}
}
function mapDispatchToProps(dispatch){
return {
increment:number=>dispatch(incrementAction(number)),
decrement:number=>dispatch(decrementAction(number)),
asyncIncrement:number=>dispatch(asyncIncrementAction(number)),
}
}
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
//2、简写
export default connect(state=>({count:state}),{increment:incrementAction,decrement:decrementAction,asyncIncrement:asyncIncrementAction})(CountUI)
//components/countUI.jsx
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { Button, InputNumber } from 'antd'
class Count extends Component {
increment=()=>{
let {value} = this.InputNumber
this.props.increment(value*1)
}
decrement=()=>{
let {value} = this.InputNumber
this.props.decrement(value*1)
}
asyncIncrement=()=>{
let {value} = this.InputNumber
this.props.asyncIncrement(value*1)
}
render() {
return (
<div>
<span>当前结果为:{this.props.count}</span>
<div>
<Button onClick={this.increment}>+</Button>
<Button onClick={this.decrement}>-</Button>
<Button onClick={this.asyncIncrement}>异步加</Button>
</div>
<InputNumber ref={c=>this.InputNumber = c}></InputNumber>
</div>
);
}
}
export default Count;
container组件和UI组件合并的写法
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { Button, InputNumber } from 'antd'
import {connect} from 'react-redux'
import { incrementAction, decrementAction ,asyncIncrementAction} from '../redux/countAction'
class Count extends Component {
increment=()=>{
let {value} = this.InputNumber
this.props.increment(value*1)
}
decrement=()=>{
let {value} = this.InputNumber
this.props.decrement(value*1)
}
asyncIncrement=()=>{
let {value} = this.InputNumber
this.props.asyncIncrement(value*1)
}
render() {
return (
<div>
<span>当前结果为:{this.props.count}</span>
<div>
<Button onClick={this.increment}>+</Button>
<Button onClick={this.decrement}>-</Button>
<Button onClick={this.asyncIncrement}>异步加</Button>
</div>
<InputNumber ref={c=>this.InputNumber = c}></InputNumber>
</div>
);
}
}
export default connect(state=>({count:state}),{increment:incrementAction,decrement:decrementAction,asyncIncrement:asyncIncrementAction})(Count)
6.4、多组件使用
整个项目只有一个store,多个reducer需要使用combineReducers合并为一个,在connect映射时需要加上模块名
//redux/reducer/countReducer.js
import {INCREMENT,DECREMENT} from '../constant.js'
const initalState = 0
export default function (preState = initalState, action) {
const { type, data } = action
switch (type) {
case INCREMENT:
return preState + data
case DECREMENT:
return preState - data
default:
return preState
}
}
//redux/reducer/personReducer.js
import {AddPerson,DeletePerson} from '../constant.js'
const initalState = []
export default function (preState = initalState, action) {
const { type, data } = action
switch (type) {
case AddPerson:
let id = data.name+data.age;
return [{...data,id}, ...preState]
case DeletePerson:
let arr = [...preState]
let index = preState.findIndex(_=>_.id===data)
arr.splice(index,1)
return arr
default:
return preState
}
}
//redux/reducer/index.js,在此文件中进行合并
import count from './countReducer'
import personList from './personReducer'
import { combineReducers } from 'redux'
export default combineReducers({count,personList})
//actions/countAction.js
import {INCREMENT,DECREMENT} from '../constant.js'
export const incrementAction = data=>{return {type:INCREMENT,data}}
export const decrementAction = data=>{return {type:DECREMENT,data}}
export const asyncIncrementAction = data=>{
return dispach=>{
setTimeout(()=>{dispach(incrementAction(data))},3000)
}
}
//redux/actions/personAction.js
import {AddPerson,DeletePerson} from '../constant.js'
export const addPersonAction = data=>{return {type:AddPerson,data}}
export const deletePersonAction = data=>{return {type:DeletePerson,data}}
//constant.js
const DECREMENT = 'decrease'
const INCREMENT = 'increase'
const AddPerson = 'add'
const DeletePerson = 'delete'
export {DECREMENT,INCREMENT,AddPerson,DeletePerson}
//redux/index.js
import {createStore,applyMiddleware} from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import reducer from './reducer/index.js'
import thunk from 'redux-thunk'
export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))
//components/count.jsx
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { Button, InputNumber } from 'antd'
import {connect} from 'react-redux'
import { incrementAction, decrementAction ,asyncIncrementAction} from '../redux/actions/countAction.js'
class Count extends Component {
increment=()=>{
let {value} = this.InputNumber
this.props.increment(value*1)
}
decrement=()=>{
let {value} = this.InputNumber
this.props.decrement(value*1)
}
asyncIncrement=()=>{
let {value} = this.InputNumber
this.props.asyncIncrement(value*1)
}
render() {
return (
<div>
<span>当前结果为:{this.props.count}</span>
<div>
<Button onClick={this.increment}>+</Button>
<Button onClick={this.decrement}>-</Button>
<Button onClick={this.asyncIncrement}>异步加</Button>
</div>
<InputNumber ref={c=>this.InputNumber = c}></InputNumber>
</div>
);
}
}
export default connect(state=>({count:state.count}),{increment:incrementAction,decrement:decrementAction,asyncIncrement:asyncIncrementAction})(Count)
//components/person.jsx
import React, { Component } from 'react'
import 'antd/dist/antd.css'
import { Input, Button } from 'antd'
import { connect } from 'react-redux'
import { addPersonAction,deletePersonAction } from '../redux/actions/personAction'
class Person extends Component {
addPerson = () => {
let data = { name: this.name.input.value, age: this.age.input.value }
this.props.addPerson(data)
}
deletePerson = (id) => {
this.props.deletePerson(id)
}
render() {
return (
<div>
<div>
<span>person总数量:{this.props.personList.length}</span>
<span>求和:{this.props.count}</span>
</div>
<div>
<span >姓名:</span>
<Input ref={c => this.name = c}></Input>
<span >年龄:</span>
<Input ref={c => this.age = c}></Input>
<Button onClick={this.addPerson}>新增</Button>
</div>
<div>
<ul>
{this.props.personList.map(_ => {
return (
<li key={_.id}>
<span>{_.name}</span>
<span>{_.age}</span>
<Button onClick={e=>this.deletePerson(_.id)}>删除</Button>
</li>
)
})}
</ul>
</div>
</div>
)
}
}
export default connect(state => ({ personList: state.personList,count:state.count }), { addPerson: addPersonAction,deletePerson: deletePersonAction})(Person)
6.5、使用redux开发者工具
在浏览器中安装插件后,然后在项目里进行安装,npm i redux-devtools-extension安装,然后在代码中createStore的第二个参数进行调用
7.hooks
函数式组件常用的hooks,createState,createEffect,createRef
import { Fragment, useState,useEffect,useRef } from 'react'
import ReactDOM from 'react-dom';
export default function FunctionalTest() {
const [name, setName] = useState('z1')
function changeName() {
// setName('z2')
setName((value)=>value+'zzzzz')
}
//将需要在componentWillUnMount中执行的函数放在第一个参数返回值中执行,后面数组参数为监听的变量
useEffect(initInterval,[name])
function initInterval(){
// console.log(1111)
let timer = setInterval(()=>console.log(1111),1000)
return ()=>clearInterval(timer)
}
function clearInterval(timer){
clearInterval(timer)
}
const myRef = useRef()
function alertInput(){
alert(myRef.current.value)
}
return (
<Fragment>
<div>
{name}
</div>
<button onClick={changeName}>修改名称</button>
<input ref={myRef}></input>
<button onClick={alertInput}>refTest</button>
</Fragment>
)
}
8、context
使用context可以进行隔代传参,非常方便。主要使用方法为使用createContext创建,然后使用在祖先组件中使用Provider提供参数,在需要使用的子孙组件中使用Consumer或contextType接收
//Parent.jsx
import React, { Component, Context } from 'react'
import { Button } from 'antd'
import Middle from './Middle'
import './index.css'
import { Provider } from './context.js'
import { Provider as Provider2 } from './context2'
export default class Parent extends Component {
state = {
number: 0,
number2:5
}
addNumber = () => {
this.setState(
preState => ({ number: ++preState.number }))
}
addNumber2 = () => {
this.setState(
(preState) => {
return { number2: ++preState.number2 }
})
}
render() {
return (
<div className='parent-part'>
<div>--------------Parent------------</div>
<div>number:{this.state.number}</div>
<Button type='primary' onClick={this.addNumber}>增加</Button>
<div>number2:{this.state.number2}</div>
<Button type='primary' onClick={this.addNumber2}>增加</Button>
<Provider value={{ number: this.state.number, addNumber: this.addNumber }}>
<Provider2 value={{ number2: this.state.number2, addNumber2: this.addNumber2 }}>
<Middle></Middle>
</Provider2>
</Provider>
</div>
)
}
}
//Middle.jsx
import React, { Component } from 'react';
import Child1 from './Child1'
import Child2 from './Child2'
import './index.css'
class Middle extends Component {
render() {
return (
<div className='middle-part'>
<div>--------------Middle------------</div>
<Child1>
</Child1>
<Child2>
</Child2>
</div>
);
}
}
export default Middle;
//Child.jsx
import React, { Component } from 'react'
import { Button } from 'antd'
import './index.css'
import { Consumer } from './context.js'
import { Consumer as Consumer2 } from './context2.js'
export default function Child() {
return (
<Consumer>
{context => {
return (
<Consumer2>
{context2 => {
return (
<div className='child-part'>
<div>--------------Child1,使用Consume接收------------</div>
<div>父组件number:{context.number}</div>
<Button onClick={context.addNumber}>修改父组件number</Button>
<div>父组件number2:{context2.number2}</div>
<Button onClick={context2.addNumber2}>修改父组件number2</Button>
</div>
)
}}
</Consumer2>
)
}}
</Consumer>
)
}
//Child2.jsx
import React, { Component } from 'react'
import { Button } from 'antd'
import './index.css'
import { parentContext } from './context.js'
export default class Child extends Component {
static contextType = parentContext;
render() {
return (
<div>
<div className='child2-part'></div>
<div>--------------Child2,使用ContextType接收------------</div>
<div>父组件number:{this.context.number}</div>
<Button onClick={this.context.addNumber}>修改父组件number</Button>
</div>
)
}
}
//context.js
import { createContext } from "react";
export const parentContext = createContext({
number:0,
addNumber:()=>{}
})
export const {Provider,Consumer} = parentContext
//context2.js
import { createContext } from "react";
export const parentContext2 = createContext({
number2:0,
addNumber2:()=>{}
})
export const {Provider,Consumer} = parentContext2