六、使用redux管理数据

一、安装redux 、redux-pack、redux-thunk

npm i redux redux-pack redux-thunk
参考网站:reduxredux-thunkredux-pack

二、在src/config下新建store.ts文件写入

import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk'; // 实现异步发送请求
import { middleware as reduxPackMiddleware } from 'redux-pack';
import rootReducer from '../reducers';

const store = createStore(
  rootReducer,
  applyMiddleware(thunkMiddleware, reduxPackMiddleware)
);

export default store

三、新建src/actions文件夹

actions 文件夹下index.ts只负责统一导出
actions文件夹下新建user.ts写如如下代码

import { api } from '../config';
import { wrapServer } from '../config/axios'

export enum ACTION_TYPE {
  LOGIN = 'LOGIN',
  LOGOUT = 'LOGOUT'
}

export const login = (data: any) => {
  return {
    type: ACTION_TYPE.LOGIN,
    promise: wrapServer({
      method: 'post',
      url: api.login,
      data,
    }),
  };
};

export const logout = () => {
  return {
    type: ACTION_TYPE.LOGOUT,
    promise: wrapServer({
      method: 'post',
      url: api.logout,
    }),
  };
};

export default {
  ACTION_TYPE,
};

注:actions文件夹下不同文件负责不同模块下的请求,如果有新的模块可以在actions文件夹下新建相对应的文件,在同一模块下新增请求需要在ACTION_TYPE添加一个唯一type,导出一个新的请求。

四、新建src/reducers文件夹

新建index.tsx文件写入如下代码(每新增一个reducers文件下的文件需要在该文件内通过combineReducers合并)

import { combineReducers } from 'redux';

import { default as user } from './user';

export default combineReducers({
  user,
})

新建user.ts文件写入如下代码(在这里可以对返回后的数值进行处理)

import { handle } from 'redux-pack';
import { User } from '../actions';

const { ACTION_TYPE } = User;

// 写入请求前默认数值
const initialState = {
  user: {}
}
 
export default (state = initialState, action: any) => {
  const { type, payload } = action;
  switch (type) {
    // 根据唯一type来匹配相应action 
    case ACTION_TYPE.LOGIN:
      return handle(state, action, {
        start: prevState => ({ ...prevState, loading: true}),
        finish: prevState => ({ ...prevState, loading: false }),
        failure: prevState => ({ ...prevState, user: state.user }),
        success: prevState => ({ ...prevState, user: payload.data }),
      });
   default:
      return state;
  }
}

五、还需在src/App.tsx文件下引入Provider和store

修改src/App.tsx文件

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
} from "react-router-dom";
import  Login from './pages/login';
import Layout from './layout';
import { Provider } from 'react-redux';
import store from './config/store';
import 'rsuite/dist/styles/rsuite-default.css';
import "./App.css";

const App = () => {
  return (
    <Provider store={store}>
    <Router>
      <Switch>
        <Route exact path="/login" 
        component = {(props: any) => <Login { ...props } /> }
        />
        <Route path="/" 
        component = {(props: any) => <Layout { ...props } /> }
        />
      </Switch>
    </Router>
    </Provider>
  );
};

export default App;

六、在页面发送请求接收数据
在src/pages/login修改index.tsx文件
引入connect

import { connect } from 'react-redux';

发送及从redux获取参数

const mapDispatchToProps = (dispatch: any) => ({
  login: (value: any) => dispatch(login(value)),
});

const mapStateToProps = (user: any) => ({ user });

使用connect连接redux

export default connect(mapStateToProps, mapDispatchToProps)(Login);

页面完整代码

import React, { useState } from "react";
import { connect } from 'react-redux';
import {
  Container,
  Content,
  FlexboxGrid,
  Panel,
  Form,
  FormGroup,
  ControlLabel,
  FormControl,
  ButtonToolbar,
  Button,
  Schema
} from "rsuite";
import {
  login
} from '../../actions/user';
import { setToken } from '../../config/localStorage';
import './login.css';

const { StringType } = Schema.Types;

const mapDispatchToProps = (dispatch: any) => ({
  login: (value: any) => dispatch(login(value)),
});

const mapStateToProps = (user: any) => ({ user });

interface Props {
  history: any;
  login: (value: any) => any;
  user: any;
}

const Login = (props: Props) => {
  const [formValue, setFormValue] = useState<any>({
    name: '',
    password: ''
  });
  const [form, setForm] = useState<any>(null);
  
  // 自定义表单规则
  const asyncCheckUsername = (name: any): any => {
    console.log(name)
    return new Promise(resolve => {
      setTimeout(() => {
        if (name === 'abc') {
          resolve(false);
        } else {
          resolve(true);
        }
      }, 500);
    });
  }

  // 验证表单规则
  const model = Schema.Model({
    name: StringType()
      .isRequired('请填写用户名.')
      .addRule((value, data) => {
        return asyncCheckUsername(value);
      }, '用户已注册'),
    password: StringType()
    .isRequired('请填写密码.')
  });

  // 提交
  const handleSubmit = () => {
    form.checkAsync().then((result: any) => {
      if (!result.hasError) {
        发送登录请求
        props.login(formValue)
        .then(
          (res: any) => {
            if (!res.error) {
              // 当登录成功时时跳转到控制台首页
              setToken(res.payload.data)
              props.history.push('/')

            }
          }
        )
      }
    });
  }

  return (
    <div className="show-fake-browser login-page">
      <Container>
        <Content>
          <FlexboxGrid justify="center">
            <FlexboxGrid.Item colspan={12}>
              <Panel header={<h3>登录</h3>} bordered>
                <Form 
                 ref={(ref: any) => (setForm(ref))}
                fluid
                onChange={formValue => {
                  setFormValue(formValue)
                }}
                onCheck={formError => {
                }}
                formValue={formValue}
                model={model}
                >
                  <FormGroup>
                    <ControlLabel>用户名</ControlLabel>
                    <FormControl name="name" />
                  </FormGroup>
                  <FormGroup>
                    <ControlLabel>密码</ControlLabel>
                    <FormControl name="password" type="password" />
                  </FormGroup>
                  <FormGroup>
                    <ButtonToolbar>
                      <Button appearance="primary" onClick={handleSubmit}>登录</Button>
                      {/* <Button appearance="link">Forgot password?</Button> */}
                      <span style={{paddingLeft: 20}}>用户名:admin 密码:123456</span>
                    </ButtonToolbar>
                  </FormGroup>
                </Form>
              </Panel>
            </FlexboxGrid.Item>
          </FlexboxGrid>
        </Content>
        {/* <Footer>xxx公司</Footer> */}
      </Container>
    </div>
  );
};

export default connect(mapStateToProps, mapDispatchToProps)(Login);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容