访问时根据路由动态决定加载哪些组件,如果组件加上connect,可以获得状态(model),组件dispatch一个action到模型中,如果是同步则到Reducer,状态发生改变,谁用了connect就得到这个状态
如果是异步到effect,需要写成生成器的写法,在effect中先进行异步处理,处理完了再通过同步给到Reducer,Reducer是负责数据更新的,再通过connect传给组件。Subscription做一些初始化设置,比如loadding框显示转圈,数据请求完后隐藏转圈
subscription的使用:history(路由地址) 进行各种初始化设置
异步操作例子
需求是:Cinema组件做数据缓存,如果是第一次进入页面,走Ajax请求异步请求数据,后面再次进入该页面直接走缓存
先用connect高阶函数包裹Cinema组件,这样Cinema组件才能拿到状态,在Cinema组件中发起diapatch,model里的effects里的函数接收到
在model异步函数effects中请求数据,yield call一个函数,这个函数是service里的,service请求数据,model拿到之后再put给reducers,再传给页面
代码展示:
要打开入口文件index.js的model
Cinema.js
import { connect } from "dva";
import React, { Component } from "react";
class Cinema extends Component {
componentDidMount() {
if (this.props.list.length === 0) {
// dispatch一个action,action解构赋值的结果是type和payload,这里只有type
this.props.dispatch({
type: "maizuo/getCinemaList",
});
} else {
console.log("缓存",this.props.list);
}
}
render() {
return (
<div>
<ul>
{this.props.list.map((item) => (
<li key={item.cinemaId}>{item.name}</li>
))}
</ul>
</div>
);
}
}
// 可以将mapStateToProps函数写外面,也可以直接写在connect里面
const mapStateToProps = (state) => {
// console.log(state,333355);
// 将对象名为list数据传给Cinema组件,组件的props能接收到名为list的数据
return { list: state.maizuo.list };
};
// 如果要简写省去return,不能要{}包裹,会被当成函数体而不是对象,所以要在外面用()包起来表示一个整体的东西
// 错误写法
// const mapStateToProps = (state) =>
// { list: state.maizuo.list}
// 正确写法
// const mapStateToProps = (state) => ({list: state.maizuo.list})
export default connect(mapStateToProps)(Cinema);
models/maizuo.js
import {getCinemaListService} from "../services/maizuo"
export default {
namespace: "maizuo",
state: {
isShow: true,
list: [],
},
subscriptions: {
setup({ dispatch, history }) {
console.log("初始化");
},
},
reducers: {
hide(prevState, action) {
// 这里合并而不直接更改数据是因为prevState是不可变的,保证状态不可变
// 所以我们要深复制一遍,再return出去(同名的直接覆盖)
return { ...prevState, isShow: false };
},
show(prevState, action) {
return { ...prevState, isShow: true };
},
changeCinemaList(prevState,{type,payload}){
// 把payload数据放到命名为list的里去
return{...prevState,list:payload}
}
},
effects: {
// 第一个参数是action可以解构赋值成type和payload
*getCinemaList(action, { call, put }) {
// call进行异步取数据,put发送新的action到reducers中
var res = yield call(getCinemaListService);
console.log(res);
// put参数也是action对象,解构赋值为type和payload
yield put({
type: "changeCinemaList",
payload: res.data.data.cinemas,
});
},
},
};
services/maizuo.js
import request from "../utils/request";
export function getCinemaListService() {
return request(
"https://m.maizuo.com/gateway?cityId=330100&ticketFlag=1&k=4103390",
{
headers: {
"X-Client-Info":'{"a":"3000","ch":"1002","v":"5.2.0","e":"16460383564094280654127105","bc":"330100"}',
"X-Host": "mall.film-ticket.cinema.list",
},
}
);
}
如果请求的网站有跨域怎么办,比如我们之前请求的网站是卖座网无跨域限制,现在我们在center页面中请求猫眼网(有跨域)的数据,在.webpackrc文件(支持dev-server、反向代理)中配置反向代理,这个文件必须是json格式,所以changeOrigin得加双引号,将changeOrigin(改变源)设置为true,将这个文件改成json
在猫眼网控制台的网络找到请求网址
将开头剪切到反向代理中
一朝/ajax进行请求时,反向代理转发到target地址,由服务端发请求,它们无跨域限制
请求另一个地址例子:
第二个例子的代码:
.webpackrc
{
"proxy": {
"/api": {
"target": "https://i.maoyan.com",
"changeOrigin": true
}
}
}
Center.js
import React, { Component } from "react";
import { withRouter } from "dva/router";
import request from "../utils/request"
export default class Center extends Component {
componentDidMount(){
request(
"/api/mmdb/movie/v3/list/hot.json?ct=%E6%9D%AD%E5%B7%9E&ci=50&channelId=4"
).then((res) => {
console.log(res.data);
});
}
render() {
return (
<div>
Center
{/* */}
{/* 不传props的情况下可以使用withRouter */}
<WithChild />
</div>
);
}
}
class Child extends Component {
render() {
return (
<div>
<button
onClick={() => {
// console.log(this.props)
localStorage.removeItem("token");
this.props.history.push("/login");
}}
>
退出登录
</button>
</div>
);
}
}
// 组件首字母要大写,注意
const WithChild = withRouter(Child);