React笔记
王银浩1年前 (2022-07-26)react54
1.数据绑定
2.事件绑定
3.生命周期
4.组件:组件通信
5.vue-cli
路由;vue-router
数据交互:axois
状态:vuex
UI:element-ui vant
react
1.创建项目
npm i create-react-app -g //全局安装脚手架 5.0 node(14+)win7(node12) win10(node卸载了安装新的node)
cnpm i yarn -g //安装yarn 包管理工具
create-react-app demo //创建项目 demo项目名称
cd demo
npm start //localhost:3000
目录结构
-demo
-build 打包后生成的文件夹,类似vue的dist
-node_modules 依赖包
-public 静态资源
index.html 唯一的页面
.gitignore 远程仓库不需要备份文件
package.json 项目管理文件
readme.md
-src 源文件
index.js 入口文件
App.js 根组件
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);
App.js
function App() {
return (
<div>
123
</div>
);
}
export default App;
2.介绍
1.react 是FaceBook开发和维护的;
2.react做的是SPA;
3.react 采用的是JSX语法。
//JSX -js +xml
<father></father>
let div='<div></div>'
let div2=<div>
<span>123</span>
</div>
function aa(){
}
4.react是单向数据流框架
5.react核心思想:一切皆组件。
3.数据绑定(JSX语法)
1.非表单元素绑定数据 {}
<div>name:{name}</div>
<div>price:{price.toFixed(2)}</div>
<div>{a > 20 ? '大数' : '小数'}</div>
2.属性绑定:{}
<img src={imgUrl} alt="" />
<a href={company.url} title={company.name} aa={company.name}>
<img src={company.logo} alt="" />
</a>
3. 在react中,对象和数组是不能直接展示到页面的,需要通过JSON.stringify()
<div>company:{JSON.stringify(company)}</div>
4. 条件渲染 {三元判断} 如果不出现任何节点的话用null
{
arr.length > 0 ? <div>有数据{name}</div> : <div>暂无数据</div>
}
{
isshow ? <div>弹框</div> : null
}
5. jsx中,遇到了<使用XML解析,遇到了{使用js解析
6.jsx写注释
{/* 6.jsx写注释 */}
7.列表渲染 {arr.map(item=>return 节点)}
<ul>
{list.map(item => {
return <li key={item.id}>{item.name}</li>
})}
</ul>
8.动态类名
8.1 react 中css使用类名用className代替class
<div className="box"></div>
8.2 动态类名: {三元}
<div className={isRed ? 'block red' : 'block orange'}></div>
9.动态行间样式 style={json}
<div style={{ color: color, fontSize: '30px' }}>从前有座山,山里有座庙</div>
4.组件注册
函数组件
import React from 'react'
export default function Fn() {
return (
<div>
</div>
)
}
类组件
import React, { Component } from 'react'
export default class Fn extends Component {
render() {
return (
<div>
</div>
)
}
}
函数组件 VS 类组件
类组件有生命周期,有state,通过this.props接收父组件传递的数据;
函数组件没有生命周期,没有state,通过props接收父组件传递的数据.
函数组件是不需要实例化的,他只是纯计算,渲染方面优于类组件。
函数组件 类组件
生命周期 没有 有
父传子 props接收 this.props接收
state 没有 有
函数组件是不需要实例化的,他只是纯计算,渲染方面优于类组件。
实际场景:
home 类组件[需要生命周期、state]----------------------业务组件
banner 函数组件【不需要生命周期、不需要state,数据从父组件传递到props上即可】--木偶组件
cate
5.state
// 1.只有state的数据变了,页面才会渲染,在页面中用到的变量都在state中声明
// 2.读取state的数据,建议使用 let { name, age, sex,num } = this.state
// 3.如果state要全部传递给子组件,建议使用{...this.state}
/* 4.修改state state不允许直接修改
4.1 修改state需要调用setState(),慎用 ++
4.2 修改数组,三步走:1取2做3放.如果做添加,还可以使用 ...
4.3 修改json,使用 ...
4.4 setState()会引起render()重新执行,所以在render中不能使用setState(),否则会陷入死循环。
4.5 this.setState()是异步的,如果想要取到修改后的新值,需要在回调函数中处理
*/
6.事件绑定
1.如何绑定事件?
onClick={()=> this.fn() }
onClick={this.fn.bind(this)} bind第一个参数是调用该函数的对象,一般传this
eg:
<div className='box'>
<h3>如何绑定事件?</h3>
<button onClick={() => this.fn()}>点击触发fn</button>
<button onClick={this.fn.bind(this)}>bind-点击触发fn</button>
</div>
2.如何传参?
箭头函数:正常传递
bind: 第1个实参传递给了方法的this对象,第2个实参对应第1个形参
eg:
<div className='box'>
<h3>如何传参?</h3>
<button onClick={() => this.add(3, 5)}>3,5</button>
<button onClick={this.add.bind(this, 10, 20)}>bind-10,20</button>
</div>
3.event对象如何获取?
显示传参: 箭头函数的参数就是event
隐示传参:bind 的最后一个参数就是event,但是不用写
eg:
<div className='box'>
<h3>event对象如何获取?</h3>
<button onClick={(e)=>this.getEvent(e)}>箭头函数获取event</button>
<button onClick={this.getEvent.bind(this)}>bind 获取event</button>
<button onClick={(e)=>this.getEvent2(10,e)}>箭头函数:10,e</button>
<button onClick={this.getEvent2.bind(this,20)}>bind: 20 e</button>
</div>
4.如何阻止传播 阻止默认?
阻止默认 e.preventDefault()
阻止传播 e.stopPropagation()
1.props
// 1.props读取建议使用 let {name,age}=props
// 2.props 如果要全部传递给子组件,使用... eg: <ChildChild {...props}></ChildChild>
// 3.props不仅可以传递数据,还可以传递方法
// 4.父传子,父组件通过props将数据传给子组件;子传父,父组件通过props将方法传递给了子组件
/* 5.对于类定义组件来说,构造函数中是不能直接使用this.props。
如果你想要在构造函数中使用props,那么需要super(props)
super() 目的是为了调用父类的构造函数;super(props)是为了在构造函数中使用this.props */
// 6.组件嵌套的内容在props.children
2.父子组件通信
父传子
父组件通过props将数据传给子组件
父组件
<ChildFn arr={this.state.arr}></ChildFn>
子组件
let { arr } = props; //接收使用
子传父
父组件通过props将方法传递给了子组件
父组件
<ChildFn del={index=>this.del(index)}></ChildFn>
子组件
let { del } = props; //接收使用
<button onClick={()=>del(index)}>删除</button>
3.ref
1.获取DOM节点
constructor() {
super()
// 1.1 创建一个ref对象
this.aa = React.createRef()
}
{/* 1.2 将ref对象绑定到节点上 */}
<div ref={this.aa} style={{ width: "100px", height: "100px", border: "1px solid #ccc" }}></div>
changeBg(color) {
// 1.3 通过 this.aa.current 获取节点
this.aa.current.style.background = color;
}
2.父组件获取子组件的实例
constructor() {
super()
// 2.1 创建一个ref对象
this.child=React.createRef()
}
{/* 2.2 将ref对象绑定到子组件上 */}
<Child ref={this.child}></Child>
changeName(name){
//修改子组件的值
// 2.3 通过 this.child.current 获取子组件实例
this.child.current.changeName(name)
}
4.表单绑定
| 取值 | 赋值 | ||
|---|---|---|---|
| input type="text" | e.target.value | value | |
| input type="radio" | e.target.value | checked | |
| input type="checkbox" | e.target.value | e.target.checked | checked |
| select | e.target.value | value | |
| textarea | e.target.value | value |
| 受控组件 | 非受控组件 | |
|---|---|---|
| 实时验证 | 是 | 否 |
| 一次性验证 | 是 | 是 |
| 实现禁用状态 | 是 | 否 |
| 特殊格式的输入 | 是 | 否 |
| 一个数据多个输入 | 是 | 否 |
input的数据和state的数据挂钩,这样的input叫受控组件;
input的数据和state的数据不挂钩,这样的input叫非受控组件
受控组件 :实时验证 实现禁用状态 特殊格式的输入(身份证号) 一个数据多个输入【推荐】
非受控组件:一次性验证 实现不了禁用状态 做不了特殊格式的输入(身份证号)实现不了一个数据多个输入的
5.生命周期
初始期:
*constructor:初始化数据
UNSAFE_componentWillMount:渲染之前 了解即可【已弃用】
*render:渲染期 根据数据,生成虚拟DOM节点,渲染到页面
*componentDidMount:组件挂载完成:ajax window|document绑定事件、jquery
更新期:state props
*shouldComponentUpdate(nextProps,nextState) :判断是否更新
不写:更新期: componentWillUpdate->render->componentDidUpdate
写了
没有return 报错
return true 更新期:
shouldComponentUpdate-->componentWillUpdate->render->componentDidUpdate
return false 更新期: shouldComponentUpdate
UNSAFE_componentWillUpdate:更新之前,了解即可【已弃用】
render:渲染期
当调用setState(),react会进入和解过程。根据新的状态生成一棵新的虚拟DOM树,和原本的DOM树做diff算法,对比哪些节点发生了改变。
计算出最小的更新算法,开始更新。
componentDidUpdate:更新完成
销毁期
*componentWillUnmount:销毁之前 清除计时器,清除window|document上的方法
6.组件优化
1.componentWillUnmount
2.父子组件渲染问题
父组件有很多数据,eg:name,num,arr ...
子组件展示父组件的部分数据,eg:num
当父组件num变了,我们希望子组件渲染,如果是父组件name变了,我们希望子组件不要渲染。
父组件传递数据给子组件
{/* 1.shouldComponentUpdate */}
<ChildClass num={num}></ChildClass>
{/* 2.PureComponent */}
<ChildClass2 num={num} json={json} ></ChildClass2>
{/* 3.React.memo() */}
<ChildFn num={num} json={json}></ChildFn>
1.shouldComponentUpdate 类定义组件
子组件通过shouldComponentUpdate 实现优化
shouldComponentUpdate(nextProps,nextState){
// console.log("nextProps:",nextProps);
// console.log("props:",this.props);
if(nextProps.num===this.props.num){
return false;
}
return true;
}
2.PureComponent 类定义组件
import React, { PureComponent } from 'react'
export default class ChildClass2 extends PureComponent {
render() {
console.log("子组件22重新渲染了");
let {num,json}=this.props
return (
<div className='box'>
<h3>类定义子组件</h3>
<div>num:{num}</div>
<div>json:{JSON.stringify(json)}</div>
</div>
)
}
}
PureComponent 和shouldComponentUpdate如何选择
PureComponent 浅比较,对于简单类型是可以直接检测到改变。但是如果接收数据是引用类型,可能会检测不到数据的改变,所以引用类型父组件那边修改的时候需要用拷贝。
PureComponent :是state和props变了,都计算是否更新
Component state|props变了,都不计算,直接更新
假设一个类定义组件只有props,使用PureComponent * [如果加上仓库,就都是使用PureComponent]
假设一个类定义组件只有state,就不需要计算是否需要更新,直接更新即可,我们使用Component *
假设一个类定义组件state和props,我们使用shouldComponentUpdate+Component
3.React.memo() 函数组件
import React from 'react'
// React.memo()函数组件的优化方案
function ChildFn(props) {
console.log("函数组件被执行了");
let {num,json}=props
return (
<div className='box'>
<h3>函数定义子组件</h3>
<div>num:{num}</div>
<div>json:{JSON.stringify(json)}</div>
</div>
)
}
export default React.memo(ChildFn)
一个函数,参数是组件,返回值是组件。这种函数称为:高阶组件(HOC)。
React.memo是函数,他是一个高阶组件。
高阶组件的作用:增强原来组件的功能
1.组件优化
1.componentWillUnmount
2.父组件传递部分数据给子组件
1.shouldComponentUpdate [类定义组件]
2.PureComponent [类定义组件 只有props]
3.React.memo() [函数定义组件]
3.Fragment
如果一个组件的根节点,不需要,可以使用来代替
写法1:
export default function Fragment() {
return (
<React.Fragment>
<h3>这是一个组件</h3>
<p>这是内容</p>
</React.Fragment>
)
}
写法2:
import React,{Fragment} from 'react'
export default function MyFragment() {
return (
<Fragment>
<h3>这是一个组件</h3>
<p>这是内容</p>
</Fragment>
)
}
写法3:
//<Fragment></Fragment> 简写为 <></>
export default function MyFragment() {
return (
<>
<h3>这是一个组件</h3>
<p>这是内容</p>
</>
)
}
4.错误边界处理 componentDidCatch
1.封装了一个组件ErrorBoundary.jsx
import React, { Component } from 'react'
export default class ErrorBoundary extends Component {
constructor(){
super()
this.state={
haveError:false
}
}
componentDidCatch(){
this.setState({
haveError:true
})
}
render() {
let {haveError}=this.state;
return (
<>
{
haveError? <div>此处暂时无法使用</div>:this.props.children
}
</>
)
}
}
2.使用
<ErrorBoundary>
<List></List>
</ErrorBoundary>
5.HOC 高阶组件
定义:
一个函数,参数是组件,返回一个新组件。这样的函数叫高阶组件。
目的:
增强组件的功能
语法:
function log(C){
return function(){
console.log("组件被加载了");
return (
<>
<header className='header'></header>
<C></C>
</>
)
}
}
案例:withRequest
let withRequest = (url) => (C) => {
return class extends Component {
constructor() {
super()
this.state = {
arr: [],
isFinished: false
}
}
componentDidMount() {
let that = this;
$.ajax({
url,
success(res) {
that.setState({
arr: res.data,
isFinished: true
})
}
})
}
render() {
let { arr, isFinished } = this.state
return (
<>
{
isFinished ? <C arr={arr}></C> : <div>正在努力加载中。。。</div>
}
</>
)
}
}
}
export default withRequest
调用:
let NewBanner=withRequest("/mock/banner.json")(Banner)
路由react-router-dom@5.0.0
1.路由模式 HashRouter BrowserRouter
HashRouter:hash路由 BrowserRouter:history路由
import {HashRouter,BrowserRouter} from "react-router-dom"
ReactDOM.render(
<HashRouter>
<App />
</HashRouter>
,
document.getElementById('root')
);
2.路由出口 Switch
import { Switch } from "react-router-dom";
<Switch></Switch>
3.路由规则 Route
import { Route } from "react-router-dom";
<Switch>
{/* 路由规则 */}
<Route path="/login" component={Login}></Route>
<Route path="/index" component={Index}></Route>
<Route path="/list" component={List}></Route>
<Route path="/detail" component={Detail}></Route>
<Route path="/search" component={Search}></Route>
{/* 重定向 */}
<Redirect to="/login"></Redirect>
</Switch>
4.重定向 Redirect
import { Redirect } from "react-router-dom";
{/* 重定向 */}
<Redirect to="/login"></Redirect>
5.路由导航-组件
Link NavLink 没有条件的跳转就可以使用 NavLink比Link多一个activeClassName
import { Link,Navlink } from "react-router-dom";
{/* 路由导航*/}
<Link to="/search">Link搜索</Link>
<NavLink to="/search">NavLink搜索</NavLink>
activeClassName
<footer>
<NavLink activeClassName="select" to="/index/home">首页</NavLink>
<NavLink activeClassName="select" to="/index/cate">分类</NavLink>
<NavLink activeClassName="select" to="/index/shop">购物车</NavLink>
<NavLink activeClassName="select" to="/index/mine">我的</NavLink>
</footer>
6.路由导航-编程式导航
{/*
编程式导航:
this.props.history.push(path) 添加一条新的历史记录
this.props.history.replace(path)用新的历史记录替换当前历史记录
this.props.history.go(-1) 返回
this.props.history.goBack() 返回
*/}
<Button type="primary"
onClick={()=> this.props.history.push("/search")}>push 搜索</Button>
<Button type="warning"
onClick={()=>this.props.history.replace("/search")}>replace 搜索</Button>
注意:
对于非路由组件,是不能直接使用编程式导航的。
1.需要路由组件将props向下传递,非路由组件收到才可以使用history 。
<GoBack {...this.props}></GoBack>
2.withRouter
import {withRouter} from "react-router-dom"
class GoBack extends React.Component{
render(){
return (
<Icon type="left" onClick={()=>this.props.history.go(-1)} />
)
}
}
export default withRouter(GoBack);
7.路由传参
1.search传参(?)
传参
<Link to="/detail?id=1&a=2&c=3"></Link>
接受参数
let search=this.props.location.search ;//'?id=1&a=2&c=3'
解析参数1:
import querystring from "querystring"
let d=querystring.parse(search.slice(1));//{id:"1",a:"2",b:"3"}
解析参数2:
let myURL = new URL("http://localhost:3000" + search);
// myURL.searchParams 是一个Map
let id = myURL.searchParams.get("id");//1
let type = myURL.searchParams.get("a");//2
2.动态路由(:)
传参:
<Link to="/detail/1/2/3"></Link>
修改路由规则
<Route path="/detail/:id/:a/:b" component={Detail}></Route>
取参数
this.props.match.params //{id:"1",a:"2",b:"3"}
8.懒加载
1.通过React.lazy()引入
let Register = React.lazy(() => import("./pages/Register/Register"));
let Index = React.lazy(() => import("./pages/Index/Index"));
2.通过Suspense包裹路由出口
export default function App() {
return (
<Suspense fallback={<div>正在努力加载中...</div>}>
{/* 路由出口 */}
<Switch>
{/* 路由规则 */}
<Route path="/login" component={Login}></Route>
<Route path="/register" component={Register}></Route>
</Switch>
</Suspense>
);
}
9.权限
1.登录拦截
1.登录成功存一个用户信息
2.自己封装了一个组件PrivateRoute
import React, { Component } from 'react'
import {Route,Redirect} from "react-router-dom"
// 登录拦截
export default class RrivateRoute extends Component {
render() {
let info=sessionStorage.getItem("info")
return (
<>
{info ?<Route {...this.props}></Route>:<Redirect to="/login"></Redirect>}
</>
)
}
}
3.需要使用登录拦截的用PrivateRoute来定义路由规则,不需要登录拦截使用Route
<Switch>
{/* 路由规则 */}
<Route path="/login" component={Login}></Route>
<Route path="/register" component={Register}></Route>
<PrivateRoute path="/index" component={Index}></PrivateRoute>
<PrivateRoute path="/list" component={List}></PrivateRoute>
<PrivateRoute path="/detail/:id" component={Detail}></PrivateRoute>
<PrivateRoute path="/search" component={Search}></PrivateRoute>
{/* 重定向 */}
<Redirect to="/login"></Redirect>
</Switch>
2.路由独享守卫
{/* 二级路由出口 */}
<Switch>
<Route path="/index/home" component={Home}></Route>
{/* 路由渲染组件,如果直接输出组件,选择component
如果输出组件有条件,那么,使用render().使用render需要手动给路由组件添加props
*/}
<Route path="/index/mine" render={(props)=>{
console.log(props);
//取出type
let type=sessionStorage.getItem("type")
return type==='1'?<Mine {...props}></Mine>:<div>你没有权限</div>
}}></Route>
</Switch>
UI框架
ant design [PC]
ant design mobile [app]
mobile.ant.design/zh/componen…
1.安装 【5.0.0】
npm install --save antd-mobile@next
2.使用
import { Button } from 'antd-mobile'
数据交互
1.配置代理
[package.json]
{
"proxy":"http://localhost:3000",
}
2.搭建数据交互
1.安装
cnpm i axios --save
cnpm i qs --save
注意:重启前端项目
2.封装 【http.js】
和vue一样
3.所有的api 写在了【api.js】
和vue一样
redux
1.三大原则
1.单一数据源
2.state是只读的
3.只能通过纯函数修改state
2.redux生态
react(视图层)---》react-redux《---- redux(状态层)---》redux-thunk《--- 后端(数据)
使用场景:
1.非父子组件共享数据;
2.从长期效益考虑,希望更好的组件之外管理状态。
3.redux
1.安装
npm i redux --save
2.创建仓库【store/index.js】
import {
createStore
} from "redux";
let initState={
name:"妲己",
age:20
}
//reducer 纯函数
/**
* state: 上一次修改后导出的结果。对于第一次来说,没有上一次,所以让他的默认值为initState
* action:任务|动作
* {type:"changeName",name:'王昭君'}
* {type:"changeAge",age:'100'}
*/
function reducer(state=initState,action){
if(action.type==='changeName'){
state.name=action.name
}
if(action.type==='changeAge'){
state.age=action.age
}
return state
}
let store=createStore(reducer)
//导出仓库
export default store;
3.仓库方法
store.getState()
// store.getState() 读取仓库的数据
console.log(store.getState()); //{name: '妲己', age: 20}
store.dispatch(action)
//store.dispatch(action) 派发任务
store.dispatch({type:"changeName",name:'王昭君'})
store.subscribe(callback)
// 添加订阅者
/*
添加订阅者之后,一旦有新数据变化了,就需要通知订阅者。 通知的过程叫:发布订阅。
*/
var un=store.subscribe(()=>{
console.log("数据变了");
})
取消订阅者
// 取消订阅者
un()
4.action creator
//action creator [actions]
export let actions = {
changeName: (name) => {
return {
type: types.CHANGE_NAME,
name
}
},
changeAge: (age) => ({type: types.CHANGE_AGE,age})
}
store.dispatch(actions.changeName('貂蝉'))
store.dispatch(actions.changeAge(200))
5.action types
// action types
const types = {
CHANGE_NAME: "CHANGE_NAME",
CHANGE_AGE: "CHANGE_AGE"
}
4.react-redux
为了方便组件关联状态,可以借助模块react-redux.
1.安装
npm i react-redux --save
2.Provider
react-redux为了方便后期组件可以拿到状态层的state,提供了Provider组件。
// 入口文件
//引入仓库
import store from "./store"
//引入Provider组件
import { Provider } from "react-redux"
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
,
document.getElementById("root"))
3.容器型组件
3.1connect
React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来.
import React, { Component } from 'react'
import { connect } from "react-redux"
class C extends Component {
render() {
console.log(this.props);
let { name, age, changeAge, changeName } = this.props
return (
<div className='box'>
</div>
)
}
}
let mapStateToProps=(state) => {
return {
}
}
let mapDispatchToProps=(dispatch) => {
return {
}
}
// connect是一个函数,返回值是一个高阶组件
export default connect(mapStateToProps,mapDispatchToProps)(C)
3.2 mapStateToProps
mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。
作为函数,mapStateToProps执行后应该返回一个对象,里面的每一个键值对就是一个映射。
let mapStateToProps=(state) => {
return {
name: state.name,
age: state.age
}
}
3.3mapDispatchToProps
mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
如果mapDispatchToProps是一个函数,会得到dispatch
let mapDispatchToProps=(dispatch) => {
return {
changeName: (name) => {
dispatch(actions.changeName(name))
},
changeAge: age => dispatch(actions.changeAge(age))
}
}
4.容器型组件VS展示型组件
| 容器型组件 | 展示型组件 | |
|---|---|---|
| 关注点 | 逻辑[取数据、更新状态] | UI的展现 |
| 对redux是否感知 | 是 | 否 |
| 读数据 | 从redux的store中获取 | 从props中获取 |
| 写数据 | 发送redux actions | 调用props的回调 |
| 如何创建 | 通过react-redux connect创建 | 手写 |
| 是否是路由组件 | 是 | 否 |
| 是否做逻辑 | 是业务组件 | 是木偶组件 |
| 一般如何定义 | 类定义组件 | 函数定义组件 |
5.selectors
为了方便容器型组件从状态层取数据,可以借助selectors。
//导出数据 selectors [getters]
export let getName=(state)=>state.name
export let getAge=state=>state.age
组件使用:
let mapStateToProps = (state) => {
return {
// 1.取数据可以借助selector函数
name: getName(state),
age: getAge(state)
}
}
6.bindActionCreators
bindActionCreators是redux的一个自带函数,作用是将单个或多个ActionCreator转化为dispatch(action)的函数集合形式。
开发者不用再手动dispatch(actionCreator(type)),而是可以直接调用方法。
可以实现简化书写,减轻开发的负担。
【仓库中的actions】
//action creator [actions]
export let actions = {
changeName: (name) => {
return {
type: types.CHANGE_NAME,
name
}
},
changeAge: (age) => ({type: types.CHANGE_AGE,age})
}
【组件引入使用】
import { bindActionCreators } from "redux"
let mapDispatchToProps = dispatch => {
return {
// 2.取方法借助bindActionCreators() 直接一次将actions上的方法全部导入props上
aa:bindActionCreators(actions,dispatch)
}
}
使用
let { aa:{changeName,changeAge} } = this.props;
5.reducer拆分
1.在store下创建modules文件夹,里面新建了2个模块 【home.js】和【shop.js】
【home.js】
//state
const initState = {
banner: [],
list: ["首页的列表"]
}
// actionTypes
// 为了防止各个模块的types互相影响,建议每个type前面加上模块名
const types = {
HOME_CHANGE_BANNER: "HOME_CHANGE_BANNER",
HOME_CHANGE_LIST: "HOME_CHANGE_LIST"
}
//reducer
const reducer = (state = initState, action) => {
switch (action.type) {
case types.HOME_CHANGE_BANNER:
//action={type:"changeBanner",banner:[1,2,3]}
return {
...state,
banner: action.banner
}
break
case types.HOME_CHANGE_LIST:
//action={type:"changeList",list:[1,2,3]}
return {
...state,
list: action.list
}
break
default:
return state;
}
}
//actionCreactor
export const actions = {
changeBanner: (banner) => ({
type: types.HOME_CHANGE_BANNER,
banner
}),
changeList: list => ({
type: types.HOME_CHANGE_LIST,
list
})
}
//导出数据 selectors
//注意:selectors函数中的state是从组件传递过来的,组件传递的state是整个仓库的state,不是当前模块的state.
export const getBanner = state => state.home.banner
export const getList = state => state.home.list
//导出reducer
export default reducer
【shop.js】
//state
const initState = {
list: ["购物车的列表"]
}
// actionTypes
const types = {
SHOP_CHANGE_LIST: "SHOP_CHANGE_LIST"
}
//reducer
const reducer = (state = initState, action) => {
switch (action.type) {
case types.SHOP_CHANGE_LIST:
//action={type:"changeList",list:[1,2,3]}
return {
...state,
list: action.list
}
break
default:
return state;
}
}
//actionCreactor
export const actions = {
changeList: list => ({
type: types.SHOP_CHANGE_LIST,
list
})
}
//导出数据 selectors
export const getList = state => state.shop.list
//导出reducer
export default reducer
2.在仓库的入口文件 【store/index.js】合并reducer
import {
createStore,
combineReducers
} from "redux"
import HomeReducer from "./modules/home"
import ShopReducer from "./modules/shop"
let reducer = combineReducers({
// key:value key:模块名,value:该模块的reducer
home: HomeReducer,
shop: ShopReducer
})
let store = createStore(reducer)
3.注意: selectors中的state是仓库的state .
//注意:selectors函数中的state是从组件传递过来的,组件传递的state是整个仓库的state,不是当前模块的state.
export const getBanner = state => state.home.banner
export const getList = state => state.home.list
4.actionTypes不能重复
// 为了防止各个模块的types互相影响,建议每个type前面加上模块名
const types = {
HOME_CHANGE_BANNER: "HOME_CHANGE_BANNER",
HOME_CHANGE_LIST: "HOME_CHANGE_LIST"
}
6.reselect
在视图中有很多数据是经过现有的状态计算得到的。在selectors中,只要仓库的state变了,所有的selector函数都会重新执行。有些数据希望依赖的数据变了,再重新计算。那么就需要使用reselect .
1.安装
npm i reselect --save
2.引入 【store/shop.js】
import {createSelector} from "reselect"
//导出数据 selectors
export const getList = state => state.shop.list
export const getName=state=>10;
export const getAllPrice=createSelector([getList,getName],(list,name)=>{
console.log("开始计算!!!!!");
let sum = 0;
list.map(item => {
sum += item.price * item.num
})
return sum;
})
语法:
export const getAllPrice=createSelector([fn1,fn2,...],(fn1返回值,fn2返回值,...)=>{
//逻辑
})
7.中间件
组件--dispatch(action) -中间1(action) next-->-中间2(action)->中间3(action)-....->reducer(action)
语法:
function middleware(store){
return function(next){
return function(action){
//逻辑 next(action)
}
}
}
let middleware= store => next=>action=>{
//逻辑 next(action)
}
案例1:
import {
createStore,
applyMiddleware,
combineReducers
} from "redux"
let myLogger = store => next => action => {
console.group("进到我的中间件了")
console.log("本次触发的action:",action);
console.log("当前的仓库数据:",store.getState())
next(action)
console.log("修改后的仓库数据:",store.getState())
console.groupEnd()
}
let store = createStore(reducer,applyMiddleware(myLogger))
案例2:redux-logger
安装
cnpm i redux-logger --save
引入
import logger from 'redux-logger'
let store = createStore(reducer,applyMiddleware(logger))
案例3: redux-thunk
8.redux-devTools
1.安装扩展程序
码云搜索:redux-devtools
2. 安装依赖
npm install --save redux-devtools-extension
引入:【store/index.js】
import { composeWithDevTools } from 'redux-devtools-extension';
const store = createStore(reducer, composeWithDevTools( applyMiddleware(logger)));
过滤器
1.【filter/index.js】
export let filterPrice = price => price.toFixed(2)
export let filterTel = tel => tel.slice(0, 3) + "****" + tel.slice(7)
2.组件使用 引入
import { filterPrice } from '../filters'
<div>总价:{filterPrice(allPrice)}</div>
计算属性
在render中实现重复执行的计算操作。
render() {
let allPrice=this.state.list.reduce((val,item)=>val+item.price*item.num,0)
return (
<div className='box'>
<h3>计算属性</h3>
<div>总价:{allPrice}</div>
</div>
)
}