redux的使用

设计思想

(1)Web 应用是一个状态机,视图与状态是一一对应的。

(2)所有的状态,保存在一个对象里面。

基本原则

  • 整个应用只有唯一一个可信数据源,也就是只有一个 Store
  • State 只能通过触发 Action 来更改
  • State 的更改必须写成纯函数,也就是每次更改总是返回一个新的 State,在 Redux 里这种函数称为 Reducer

Redux工作流

 

store 数据仓库,通过createStore创建状态树store,通过订阅store.subscribe将state传给UI组件,用于页面渲染, 如果想改变state, 可以通过dispatch分发action, 传给store, store将旧数据的state和action传给reducer进行数据处理,返回新的state, 只要state一改变,就又会触发订阅subscribe。

下面以切换语言为例来:

首先npm 安装redux, 我这里用到了i18n语言包,所以安装了i18next和react-i18next.

npm install redux i18next react-i18next --save

// react-i18next官网:https://react.i18next.com/guides/quick-start

然后创建redux/store.ts文件

//store.ts

import { createStore } from "redux";
import languageReducer from "./language/languageReducer";

/**
 * 创建store数据中心
 * store只读,不做其他处理
 */
const store = createStore(languageReducer);

export default store;

 createStore需要传入一个reducer,创建redux/lanuage/languageReducer.ts文件

//languageReducer.ts

import i18n from "i18next";
import { CHANGE_LANGUAGE, ADD_LANGUAGE, LanguageActionTypes } from "./languageActions";

export interface LanguageState{
    language: "zh" | "en";
    languageList : {name: string, code: string}[];
}

const defaultState: LanguageState ={
    language:"zh",
    languageList: [
        {name: "中文", code: "zh"},
        {name: "English", code: "en"}
    ]
}


export default (state = defaultState, action: LanguageActionTypes) => {
    
    switch (action.type) {
        case CHANGE_LANGUAGE:
            //i18n语言切换
            i18n.changeLanguage(action.payload);
            return {...state, language: action.payload}
        case ADD_LANGUAGE:
            return {...state, languageList: [...state.languageList, action.payload]}
        default:
            return state
    }
    
}

创建redux/lanuage/languageActions.ts文件,将所有的action都放在这个文件中

//lanuageActions.ts


export const  CHANGE_LANGUAGE = 'change_language' ;
export const  ADD_LANGUAGE = 'add_language' ;

interface ChangeLanguageAction {
    type: typeof CHANGE_LANGUAGE;
    payload: "zh" | "en";
}
interface AddLanguageAction{
    type: typeof ADD_LANGUAGE;
    payload: { name: string, code : string};
}
export type LanguageActionTypes = ChangeLanguageAction | AddLanguageAction;
export const changeLanguageActionCreator = (languageCode : "zh"|"en") : ChangeLanguageAction => {
    return {
        type: CHANGE_LANGUAGE,
        payload : languageCode
    }
}

export const addLanguageActionCreator = (name: string, code: string) : AddLanguageAction => {
    return {
        type: ADD_LANGUAGE,
        payload: { name, code}
    }
}

接下来是在类组件中应用订阅store和分发dispatch, 创建pages/Header.class.tsx文件

//Header.class.tsx 

import React from "react";
import style from "./Header.module.css";
import logo from "../../assets/images/logo.svg";
import { Layout, Typography, Input,Button, Dropdown,Menu } from "antd";
import { GlobalOutlined} from '@ant-design/icons';
import {withRouter, RouteComponentProps} from 'react-router-dom';
import store from '../../redux/store';
import {LanguageState} from '../../redux/language/languageReducer';
import { withTranslation, WithTranslation } from "react-i18next";
import { addLanguageActionCreator, changeLanguageActionCreator } from "../../redux/language/languageActions";
interface State extends LanguageState{}
class HeaderComponent extends React.Component<RouteComponentProps & WithTranslation, State>{

    constructor(props) {
        super(props);
        const storeState = store.getState();
        this.state = {
            language: storeState.language,
            languageList: storeState.languageList
        }
        
    }

    handleStoreChange = ()=>{
        const storestate = store.getState();
        
        this.setState({
            language : storestate.language,
            languageList: storestate.languageList,
        })
        
    }
    
    menuClickHander = (e)=>{
        
        if(e.key === 'new'){
            //分发
            const action = addLanguageActionCreator('新语言', 'new_lang');
            store.dispatch(action);
        }else{
            //分发
            const action = changeLanguageActionCreator(e.key);
            store.dispatch(action);
        }
        
    }

    componentDidMount(){
        //订阅
        store.subscribe(this.handleStoreChange);
    }

    render(){
        // const { history} = this.props;
        const { t } = this.props;
        return <div className={style.appHeader}>
            <div className={style.inner}>
                <Typography.Text >{ t('header.slogan') }</Typography.Text>
                <Dropdown.Button
                
                overlay={
                    <Menu onClick={this.menuClickHander}>
                        {this.state.languageList.map(l => <Menu.Item key={l.code}>{l.name}</Menu.Item>)}

                        <Menu.Item key={'new'}>{t('header.add_new_language')}</Menu.Item>
                    </Menu>
                }
                icon={<GlobalOutlined/>}
                style={{marginLeft:15}} 
                >

                    {this.state.language === 'en' ? 'English' : '中文'}
                </Dropdown.Button>

                <Button.Group className={style.buttonGroup}>
                    <Button>{ t('header.register') }</Button>
                    <Button>{ t('header.signin') }</Button>
                </Button.Group>

                
            </div>
            <Layout.Header className={style.inner}>
                    <img src={logo} alt="" className={style.appLogo}/>
                    <Typography.Title level={3} className={style.title} >{ t('header.title') }</Typography.Title>
                    <Input.Search placeholder="请输入旅游的目的地" className={style.inputSearch}/>
                </Layout.Header>
            <Menu className={style.navMenu} mode={'horizontal'}>
                <Menu.Item key={1}>{t('header.home_page')}</Menu.Item>
                <Menu.Item key={2}>{t('header.weekend')}</Menu.Item>
                <Menu.Item key={3}>{t('header.group')}</Menu.Item>
                <Menu.Item key={4}>{t('header.backpack')}</Menu.Item>
                <Menu.Item key={5}>{t('header.private')}</Menu.Item>
                <Menu.Item key={6}>{t('header.cruise')}</Menu.Item>
                <Menu.Item key={7}>{t('header.hotel')}</Menu.Item>
            </Menu>
            
        </div>
    }
    
}

export const Header = withTranslation()(withRouter(HeaderComponent));

以下是i18n的配置--------------------------------------------------------------------------------------------------------------------------------------------------

i18n的配置文件config.ts

// i18n/config.ts

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import translation_zh from './zh.json';
import translation_en from './en.json';
const resources = {
    en: {
      translation: translation_en
    },
    zh: {
      translation: translation_zh
    }
  };

  i18n
  .use(initReactI18next) // passes i18n down to react-i18next
  .init({
    resources,
    lng: "zh",

    // keySeparator: false, // we do not use keys in form messages.welcome

    interpolation: {
      escapeValue: false // react already safes from xss
    }
  });

  export default i18n;
  

zh.json文件

// i18n/zh.json
{
    "header": {
        "slogan": "让旅行更幸福",
        "add_new_language": "添加新语言",
        "title": "React 旅游网",
        "search_placeholder": "请输入旅游的目的地",
        "register":"注册",
        "signin":"登陆",
        "home_page": "旅游首页",
        "weekend": "周末游",
        "group": "跟团游",
        "backpack": "自由行",
        "private": "私家团",
        "cruise": "邮轮",
        "hotel": "酒店+景点",
        "local": "当地玩乐",
        "theme": "主题游",
        "custom": "定制游",
        "study": "游学",
        "visa":"签证",
        "enterprise":"企业游",
        "high_end":"高端游",
        "outdoor":"爱玩户外",
        "insurance":"保险"
    },
    "footer": {
        "detail" : "版权所有 @ React 旅游网"
    },
    "home_page": {
        "hot_recommended": "爆款推荐",
        "new_arrival": "新品上市",
        "domestic_travel": "国内游推荐",
        "joint_venture": "合作企业",
        "start_from": "(起)"
    }
}

在App.tsx文件中引入import './i18n/config';就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值