29.9React课程
第10节:umi框架实战项目
(第10节:umi框架实战项目&)
第10节:umi框架实战项目&
Generator是异步解决方案,next执行下一个步骤
*可以放在后面也可以放在前面
Yield表示暂停
import React, { Component } from "react";
function* helloGenerator() {
yield "hello";
yield "world";
yield "ending";
}
const hg = helloGenerator();
console.log(hg.next());
console.log(hg.next());
console.log(hg.next());
console.log(hg.next());
class GeneratorPage extends Component {
render() {
return GeneratorPage;
}
}
export default GeneratorPage;
import React from "react";
import logo from "./logo.svg";
// import GeneratorPage from "./pages/GeneratorPage";
import ThunkPage from "./pages/ThunkPage";
function App() {
return (
{/* */}
);
}
export default App;
React-redux使用redux更加react,Redux-thunk支持dispatch传递函数,支持异步解决方案
import React, { Component } from "react";
import { BrowserRouter, Route, Link, Switch } from "react-router-dom";
import LoginPage from "./LoginPage";
import UserPage from "./UserPage";
import PrivatePage from "./PrivatePage";
class ThunkPage extends Component {
render() {
return (
Rudex - react-redux
登录
用户中心
{/* */}
);
}
}
export default ThunkPage;
LoginPage.js,通过thunk中间件发送异步请求
import React, { Component } from "react";
import { Redirect } from "react-router-dom";
import { connect } from "react-redux";
export default connect(
state => ({
isLogin: state.isLogin,
loading: state.loading,
error: state.error
}),
{
//thunk
// login: () => dispatch => {
// dispatch({ type: "requestLogin" });
// setTimeout(() => {
// dispatch({
// type: "requestSuccess"
// });
// }, 2000);
// }
//saga
login: name => ({ type: "login", name })
}
)(
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
name: ""
};
}
setName = event => {
this.setState({
name: event.target.value
});
};
render() {
const { isLogin, loading, login, error } = this.props;
if (isLogin) {
return ;
}
const { name } = this.state;
return (
登录
{error && {error}
}
{
login(name);
}}
>
{loading ? "登录中..." : "登录"}
);
}
}
);
UserPage.js
import React, { Component } from "react";
export default class UserPage extends Component {
render() {
return (
用户中心
);
}
}
PrivatePage.js 路由守卫权限判断,可以传递path和component以及isLogin
传入动态值
import React, { Component } from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
export default connect(state => ({
isLogin: state.isLogin
}))(
class PrivatePage extends Component {
render() {
const { path, component, isLogin } = this.props;
if (isLogin) {
return ;
}
return (
{ pathname: "/login", state: { redirect: path } }} /
);
}
}
);
store/index.js
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
//引入saga
import createSagaMiddleware from "redux-saga";
import mySaga from "./mySaga";
//创建中间件
const sagaMiddleware = createSagaMiddleware();
const initialLogin = {
isLogin: false,
loading: false,
username: "",
error: ""
};
const loginReducer = (state = { ...initialLogin }, action) => {
switch (action.type) {
case "requestLogin":
return {
...state,
loading: true
};
case "requestSuccess":
return {
...state,
isLogin: true,
loading: false
};
case "requestFailure":
return {
...state,
error: action.err
};
default:
return state;
}
};
// const store = createStore(loginReducer, applyMiddleware(thunk));
const store = createStore(loginReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(mySaga);
export default store;
index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import store from "./store";
ReactDOM.render(
,
document.getElementById("root")
);
Redux-saga解决异步请求问题
This.props获取组件属性渲染哪一个组件,重定向动态路径的路由守卫,没有登录跳转到登录页。
对Route的包装
mySaga.js,takeEvery定制任务单,任务单有login任务菜户监控执行loginHandle
action是用户传入的,login(name),返回的res与err action都可以拿到
import { call, put, takeEvery } from "redux-saga/effects";
const Userservice = {
login(name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (name === "kaikeba") {
resolve({ name: "kaikeba" });
} else {
reject("用户名或密码错误");
}
}, 1500);
});
}
};
//worker
function* loginHandle(action) {
try {
yield put({ type: "requestLogin" });
const res = yield call(Userservice.login, action.name);
yield put({ type: "requestSuccess", res });
} catch (err) {
yield put({ type: "requestFailure", err });
}
}
//watcher saga
function* mySaga() {
yield takeEvery("login", loginHandle);
}
export default mySaga;
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
//引入saga
import createSagaMiddleware from "redux-saga";
import mySaga from "./mySaga";
//创建中间件
const sagaMiddleware = createSagaMiddleware();
const initialLogin = {
isLogin: false,
loading: false,
username: "",
error: ""
};
const loginReducer = (state = { ...initialLogin }, action) => {
switch (action.type) {
case "requestLogin":
return {
...state,
loading: true
};
case "requestSuccess":
return {
...state,
isLogin: true,
loading: false
};
case "requestFailure":
return {
...state,
error: action.err
};
default:
return state;
}
};
// const store = createStore(loginReducer, applyMiddleware(thunk));
const store = createStore(loginReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(mySaga);
export default store;
Switch独占路由
入口引入
路由守卫是对route的包装,没有登录跳转到登录页,this.props可以拿到path
动态值,可以根据其他路径重定向
Redux ,createStore中接收reducers,根据action不同返回不同state
初始化状态
发送请求等待结果,改变isLogin
初始化状态
根据action的不同返回不同的state
把默认值传递给state,发送请求改变loading状态
Export default 组件中使用redux
状态与组件绑定,使用redux
Provider内部通过上下文实现,provider包裹App
属性和dispatch映射,通过connect
导出connect包裹一个组件 ,高阶组件connect就是一个装饰器两个参数mapStateToProps和mapDispatchToProps就可以在组件内部通过this.props获取状态。
绑定State和dispatch,props中有了isLogin和loading。Connect中绑定mapStateToProps和mapDispatchToProps
onClick事件,点击按钮发送dispatch
对应请求,开始requestLogin,接下来发送requestSuccess是异步的
requestLogin同步的,redux没有支持异步能力,需要通过中间件thunk支持异步
点击执行,{}是一个事件,登录进入用户中心页
Thunk支持异步
Dispatch才具有解析函数能力,dispatch有两个操作发送requestLogin和requestSuccess
路由守卫也需要绑定状态,没有dispatch的派发只依赖状态,只需要绑定状态即可。
要从this.props中获取isLogin,标签上没有绑定isLogin,根据不同isLogin去不同页面
Saga支持异步实现方式更加简便,提供effect
Call发送请求,put更新状态,takeEvery定制任务单,任务单有任务执行否则不执行
模拟用户接口,传递数据
Action.name是用户login接口接收到的参数
如果请求有错误try...catch,传递res和err,action都能拿的到
Watcher设置监听,takeEvery任务单有login任务才会去监控
Action只有dispatch发送的时候才生成,
Saga也是redux的中间件
使用中间件
Login接收一个参数,传递name,action就能获取name
使用带参数的login
带参数的login,接收用户参数
保存用户输入的name
mySaga的接口
执行requestFailure,保留报错信息
Store中实现requestFailure
Err是通过mySaga中传递来的
要在loginHandle中传入action参数,
参数的传递,点击登录传递参数name ,用户传递进来的值
通过dispatch订阅的login发送过来login和name,就可以通过action来接收name了
之后进入到saga逻辑中来
Login中会接收到一个参数,进入判断,reject中的信息可以通过err捕获
在store中可以通过action接收到err
解构出error
loginHandle是具体的行为,通过任务清单来触发函数
启动中间件,监听任务单里的任务
(umi&)
umi&
快速创建目录结构
指定css类型
创建动态路由
import React from 'react';
import { Router as DefaultRouter, Route, Switch } from 'react-router-dom';
import dynamic from 'umi/dynamic';
import renderRoutes from 'umi/lib/renderRoutes';
import history from '@tmp/history';
import { routerRedux } from 'dva';
const Router = routerRedux.ConnectedRouter;
const routes = [
{
path: '/',
component: require('../../layouts/index.js').default,
routes: [
{
path: '/404',
exact: true,
component: require('../404.js').default,
},
{
path: '/about',
exact: true,
component: require('../about.js').default,
title: 'about page',
Routes: [
require('../../routes/PrivateRoute.js').default,
require('../../routes/Test.js').default,
],
},
{
path: '/goods',
exact: true,
component: require('../goods/index.js').default,
},
{
path: '/goods',
exact: true,
component: require('../goods.js').default,
},
{
path: '/',
exact: true,
component: require('../index.js').default,
},
{
path: '/login',
exact: true,
component: require('../login.js').default,
},
{
path: '/users',
exact: false,
component: require('../users/_layout.js').default,
routes: [
{
path: '/users',
exact: true,
component: require('../users/index.js').default,
},
{
path: '/users/:id',
exact: true,
component: require('../users/$id.js').default,
},
{
component: () =>
React.createElement(
require('/Users/kele/.config/yarn/global/node_modules/umi-build-dev/lib/plugins/404/NotFound.js')
.default,
{ pagesPath: 'pages', hasRoutesInConfig: false },
),
},
],
},
{
path: '/:post/commit',
exact: true,
component: require('../$post/commit.js').default,
},
{
path: '/:post',
exact: true,
component: require('../$post/index.js').default,
},
{
component: () =>
React.createElement(
require('/Users/kele/.config/yarn/global/node_modules/umi-build-dev/lib/plugins/404/NotFound.js')
.default,
{ pagesPath: 'pages', hasRoutesInConfig: false },
),
},
],
},
{
component: () =>
React.createElement(
require('/Users/kele/.config/yarn/global/node_modules/umi-build-dev/lib/plugins/404/NotFound.js')
.default,
{ pagesPath: 'pages', hasRoutesInConfig: false },
),
},
];
window.g_routes = routes;
const plugins = require('umi/_runtimePlugin');
plugins.applyForEach('patchRoutes', { initialValue: routes });
export { routes };
export default class RouterWrapper extends React.Component {
unListen() {}
constructor(props) {
super(props);
// route change handler
function routeChangeHandler(location, action) {
plugins.applyForEach('onRouteChange', {
initialValue: {
routes,
location,
action,
},
});
}
this.unListen = history.listen(routeChangeHandler);
routeChangeHandler(history.location);
}
componentWillUnmount() {
this.unListen();
}
render() {
const props = this.props || {};
return {renderRoutes(routes, props)};
}
}
$id.js,通过props获取动态路由的值
import styles from "./$id.css";
export default function(props) {
const { match } = props;
console.log(match);
return (
Page $id:: {match.params.id}
);
}
嵌套路由
_layout.js
import styles from './_layout.css';
export default function(props) {
return (
Layout for ./users
{
props.children
}
);
}
页面跳转
import styles from './index.css';
export default function() {
return (
Page index
);
}
index.js
import styles from './index.css';
export default function() {
return (
Page index
);
}
dva仓库要有namespace,export导出一个对象,接收状态要通过namespace来区分。
Reducers管理状态返回最新的状态,接收state和action,effects异步操作不能直接修改state通过reducer来修改state。
Namespace根据模块来区分创建很多的状态。
Action是触发器,通过type属性匹配到reducer或effec,playload属性则是数据体,传递参数给reducer和effect
(创建umi项目&)
创建umi项目&
dva使用model,dva内置connect,把状态映射到组件中。
Connect第一个参数来映射属性的,mapStateToProps,返回一个对象。
取到model中的goods,通过state.goods获取属性值。State是所有的状态集。
goodList映射到props中,通过props直接解构{goodList},打印数组。
创建新的namespace
更新数据状态,解构当前state接收新的另外新的对象title,通过action.payload参数传递
组件中映射addGoods,通过绑定dispatch获得,要接收一个参数,返回action即可。
Payload传递参数
addGoods要解析出来,function({addGoods}),直接使用addGoods即可。
addGoods发送dispatch在Model的reducers接收state和action,返回对象的更新
请求接口,通过effect进行处理。
Result就是数据
getList参数结构出来动作action和call与put
Axios发送请求
Call拿到结果通过put来更新数据。Action对应一个reducers来更新state,payload接收过来的参数就是res拿到的数据。
初始状态更新返回的action.payload,就是res的payload
函数式组件,组件渲染前就进行渲染需要通过hook实现
调用getList,通过命名空间调用effect中的方法
函数式组件,接收回调,执行就可以了
Dva-loading内置加载状态,直接把loading映射到组件上即可。
获取loading来使用,如果有goods命名空间加载,加载完毕置空
开启antd
基本布局,所有的子组件通过props放到content中,共同用于导航与页尾
404页面,exception已经保证好了样式
登录校验组件
Connect包裹函数组件
登录失败
通知组件,根据不同状态码弹窗