GVKun编程网logo

[译文&摘抄]在 React & Redux 中使用 AJAX 轮询(react-redux-router)

17

本篇文章给大家谈谈[译文&摘抄]在React&Redux中使用AJAX轮询,以及react-redux-router的知识点,同时本文还将给你拓展Flux、Redux到react-redux衍变发展之

本篇文章给大家谈谈[译文&摘抄]在 React & Redux 中使用 AJAX 轮询,以及react-redux-router的知识点,同时本文还将给你拓展Flux、Redux到react-redux衍变发展之Redux解读、react redux react-redux使用方式(一)、react 系列(五)在 React 中使用 Redux、react+react-router+redux+react-redux构建一个简单应用等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

[译文&摘抄]在 React & Redux 中使用 AJAX 轮询(react-redux-router)

[译文&摘抄]在 React & Redux 中使用 AJAX 轮询(react-redux-router)

原文地址:AJAX POLLING IN REACT WITH Redux
原文作者:Josh M
译文出自:掘金翻译计划
转自:https://juejin.im/post/5a43b6da5188257d167a7aef
译者:刘嘉一
校对者:yoyoyohamapi,FateZeros

目标:把一些有时序依赖的状态从服务端同步到客户端

方式:结合 React 组件的生命周期方法、 Redux 的 Action 以及 setTimeout 函数

HOW:

reducer 文件:

const initialState = {  
    data: {},isFetching: false // 通过这个键值,判断是否正在获取数据
};

export function data (state = initialState,action) {  
    switch (action.type) {
    case DATA_FETCH_BEGIN: {
        return { ...state,isFetching: true };
    }
    case DATA_FETCH_SUCCESS: {
        return { isFetching: false,data: { ...state.data,action.payload }};
    }
    case DATA_FETCH_ERROR: {
        return { ...state,isFetching: false };
    }
    default:
        return state;
}

Action 文件

export function dataFetch() {  
  return {
    [CALL_API]: {
      types: [DATA_FETCH_BEGIN,DATA_FETCH_SUCCESS,DATA_FETCH_ERROR],endpoint: 'api/data/'
    }
  };
}

如何定义组件?

import React from 'react';  
import {connect} from 'react-redux';  
import {bindActionCreators} from 'redux';  
import * as DataActions from 'actions/DataActions';

// 组件需要哪些 Redux 全局状态作为 props 传入?
function mapStatetoProps(state) {  
    return {
        data: state.data.data,isFetching: state.data.isFetching
    };
}

// 组件需要哪些 Action 创建函数作为 props 传入?
function mapdispatchToProps(dispatch) {  
    return {
        dataActions: bindActionCreators(DataActions,dispatch)
    };
}

@connect(mapStatetoProps,mapdispatchToProps)
export default class AppContainer {  
    componentwillReceiveProps(nextProps) {
        if (this.props.data !== nextProps.data) {

            clearTimeout(this.timeout);

            // 你可以在这里处理获取到的数据

            if (!nextProps.isFetching) {
                this.startPoll();
            }
        }

    }

    componentwillMount() {
        this.props.dataActions.dataFetch();
    }

    componentwillUnmount() {
        clearTimeout(this.timeout);
    }

    startPoll() {
        this.timeout = setTimeout(() => this.props.dataActions.dataFetch(),15000);
    }
}

Flux、Redux到react-redux衍变发展之Redux解读

Flux、Redux到react-redux衍变发展之Redux解读

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zeping891103/article/details/84569175

续上篇,在Flux后,为了更好的实现MVC,Redux模式出现。

不同于 Flux ,Redux 不再有 dispatcher 的概念(Store已经集成了dispatch方法)。其次它依赖纯函数来替代事件处理器(即原来Flux中Dispatcher.register((action) 注册逻辑处理这块),这个纯函数叫做Reducer。另外使用到了一个新概念 context ,在React 组件间,数据是通过 props 属性由上向下(由父及子)进行传递的,当遇到多个层级多个组件间共享一个props,这种树形的由上而下的传参方式就显得过于繁琐,context 便很巧妙的解决了这个问题,参数只需从树顶点设置一次,便可在其所有枝节点都能共享到。

看了react-redux官方源码,总结出其redux思想主要由四个部分组成:reducers、store(redux)、react-redux和view。大致画了个图,其逻辑关系如下:

为了让大家更好理解Redux思想,以设定/更改全局主题颜色为例,本demo暂不会引用官方已封装好的 ''redux''和''react-redux'' 模块,而是抽离出核心代码综合编写了一个demo。(建议先执行一遍demo,按着代码理解这些概念,会轻松很多)

源码地址:https://github.com/smallH/redux-demo.git

reducers

reducers,入参为:组件当前所在状态state,将要处理的动作 action。action通常是一个对象,由类型和值{type, value}组成,通过switch(action.type)来筛选类型。简单来说,reducers就是组件状态发生变化时主要逻辑处理的地方。其代码如下:

// reducers.js
const themeReducer = (state, action) => {
    if (!state) return {
      themeColor: ''red''
    }
    // 处理各类action,并返回最新的状态
    switch (action.type) {
      case ''CHANGE_COLOR'':
        return { ...state, themeColor: action.themeColor }
      default:
        return state
    }
}
export default themeReducer

上面代码表示,当 action 类型为''CHANGE_COLOR''时(我告诉你我要改变颜色啦),则改变颜色状态值 state.themeColor 为action.themeColor。其中{ ...state, themeColor: action.themeColor }是一种语法糖写法,表示返回一个新对象 newState ,它不仅继承了原有入参 state的数据结构和值,还顺道修改了themeColor属性值。注意哦,这种写法的好处就是实现了返回的新状态值newState 和 state 在内存中没有指向同一引用,是两个各自不想关的对象,也可以理解为深度拷贝吧。

store(redux)

核心其实就是就是官方模块中的引用的redux:

import { createStore } from ''redux''

但在本demo中我们并不直接引用,我们先来看看代码:

// redux.js
export const createStore = (reducer) => {
	let state = null
	const listeners = []; // 事件监听列表
	const subscribe = (listener) => listeners.push(listener); // 定义添加事件对外接口
	const getState = () => state; // 定义获取状态总值对外接口
	// 定义驱动 Aciton 的对外接口,每次驱动会遍历执行listeners列表里的所有事件
	const dispatch = (action) => {
		state = reducer(state, action)
		listeners.forEach((listener) => listener())
	}
	dispatch({}); // 首次初始化state
	return {
		getState,
		dispatch,
		subscribe
	}
}

该模块以reducer为入参,返回了三个带有核心功能的对象{getState, dispatch, subscribe},目的是对外提供了状态获取和更新的渠道。

getState:获取所有通过store管理的组件的状态值。

dispatch:驱动reducer执行状态更新,并遍历事件监听列表,使在状态更新后自动刷新(渲染)dom节点。

subscribe:添加需要自动刷新的dom节点的_updateProps()函数到监听列表。

react-redux

该模块比较复杂,它提供了两个高阶组件Provider和 connect 函数。在看本模块前如果不了解高阶函数的意义和context功能,可以先看一下:react系列(21)高阶组件 和 react系列(17)跨组件树传递数据 context

高阶组件Provider:很简单,主要功能是提供 context 的全局状态入参 store 设置。

// 高阶组件 Provider
export class Provider extends React.Component {
	static propTypes = {
		store: PropTypes.object,
		children: PropTypes.any
	}

	static childContextTypes = {
		store: PropTypes.object
	}

	// 通过对context调用设置store
	getChildContext() {
		return {
			store: this.props.store
		}
	}

	render() {
		return(
			<div>{this.props.children}</div>
		)
	}
}

高阶组件connect:主要功能是为了连接起视图层view和store。

import React from ''react''
import PropTypes from ''prop-types''

// 高阶组件 contect 
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
	class Connect extends React.Component {
		// 通过对context调用获取store
		static contextTypes = {
			store: PropTypes.object
		}

		constructor() {
			super()
			this.state = {
				allProps: {}
			}
		}
		
		// 第一遍需初始化所有组件初始状态
		componentWillMount() {
			const store = this.context.store
			this._updateProps()
			store.subscribe(() => this._updateProps()); // 加入_updateProps()至store里的监听事件列表
		}
		
		// 执行action后更新props,使组件可以更新至最新状态(类似于setState)
		_updateProps() {
			const store = this.context.store;
			let stateProps = mapStateToProps ?
				mapStateToProps(store.getState(), this.props) : {} // 防止 mapStateToProps 没有传入
			let dispatchProps = mapDispatchToProps ?
				mapDispatchToProps(store.dispatch, this.props) : {} // 防止 mapDispatchToProps 没有传入
			this.setState({
				allProps: {
					...stateProps,
					...dispatchProps,
					...this.props
				}
			})
		}

		render() {
			return <WrappedComponent {...this.state.allProps} />
		}
	}
	return Connect
}

高阶组件connect有三个入参:mapStateToProps, mapDispatchToProps 和 WrappedComponent。

首先需要明白,react-redux模块的一个主要目的就是可以把view和store连接起来,store存储了所有组件的状态值state和事件处理方法action,但并不代表所有的组件都需要用到全部的state和action,于组件而言,最好的办法是我告诉store我需要那些 state和action 你给我就好,mapStateToProps和mapDispatchToProps就是干这个事情的。

mapStateToProps:告诉store ,本组件渲染时所需的props值。

mapDispatchToProps :告诉store,本组件触发事件时所需的action。

WrappedComponent:将要被包装升级的原组件,最好为Dumb组件。Dumb组件是指只可以也仅可以通过props来控制组件渲染内容,它也是最符合react设计思想的组件设计,复用性高耦合性低。

现在,回过头来看看最开始的逻辑图,是不是清楚了很多。

view

即将要被渲染的组件。

import React from ''react''
import PropTypes from ''prop-types''
import { connect } from ''../react-redux''

class ThemeSwitch extends React.Component {
	// 设置所需参数
	static propTypes = {
		themeColor: PropTypes.string,
		onSwitchColor: PropTypes.func
	}

	handleSwitchColor(color) {
		if(this.props.onSwitchColor) {
			this.props.onSwitchColor(color)
		}
	}

	render() {
		return(
			<div>
		                <button
		                  style={{ color: this.props.themeColor }}
		                  onClick={this.handleSwitchColor.bind(this, ''red'')}>Style-Red</button>
		                <button
		                  style={{ color: this.props.themeColor }}
		                  onClick={this.handleSwitchColor.bind(this, ''blue'')}>Style-Blue</button>
      		        </div>
		)
	}
}

const mapStateToProps = (state, ownProps) => {
	return {
		themeColor: state.themeColor
	}
}
const mapDispatchToProps = (dispatch, ownProps) => {
	return {
		onSwitchColor: (color) => {
			dispatch({
				type: ''CHANGE_COLOR'',
				themeColor: color
			})
		}
	}
}
ThemeSwitch = connect(mapStateToProps, mapDispatchToProps)(ThemeSwitch)

export default ThemeSwitch

结合上面的react-redux模块高阶组件的意图,看起来就很明白了,没什么好讲述的了,有问题可以留言。

最终demo运行效果:点击按钮Style-Red主题颜色变为红色,点击按钮Style-Red主题颜色变为蓝色。

最后,我们试着把上面例子中的 redux.js 和 react-redux.js 文件删除,改为直接引用官方的 ''redux'' 和 ''react-redux'':

// 安装
$ npm install redux -S
$ npm install react-redux -S

// 引用
import { createStore } from ''redux''
import { Provider } from ''react-redux''

会发现程序依然运行起来了,而且结果是一样的,棒棒的!这就是Redux模式了,而官方提供的 ''redux'' 和 ''react-redux'' 模块,只过不是对上面代码的封装和多了一些辅助插件而已,下一篇将介绍这些插件的用法。

react redux react-redux使用方式(一)

react redux react-redux使用方式(一)

在react的项目中使用redux的基本用法

文件主要分四部分

  • type
  • reducer
  • action
  • 入口index

type文件

项目中不是必须有这个文件的存在,只是为了在大型项目中利于维护将各种type独立出来。
/** 文件示例*/
export const USER_NAME = ''USER_NAME'';
export const USER_CODE = ''USER_CODE'';

reducer文件

reducer是一个函数,接收action和当前state作为参数,配合action中的type对state的值进行更新
/** 文件示例*/
import { USER_NAME, USER_CODE } from ''../type/module1'';

const userInfo = {
    userName: ''马云'',
    userCode: ''my'',
};

export function USER_INFO(state = userInfo, action) {
    switch (action.type) {
        case USER_NAME:
            return Object.assign({}, state, { userName: action.name });
        case USER_CODE:
            return Object.assign({}, state, { userCode: action.code });
        default:
            return state;
    }
}

action文件

action是改变state的唯一办法,action是一个对象其中type是必须的,其它参数可以自由设置(多数情况下这些参数就是更新state所需要的值)
/** 文件示例*/
export const USER_CODE_ACTION = (code) => {
    return {
        type: ''USER_CODE'',
        code,
    }
};

export const USER_NAME_ACTION = (name) => {
    return {
        type: ''USER_NAME'',
        name,
    }
};

入口文件

入口文件中重点是combineReducers这个辅助函数,他可以将多个reducer合并成最终的reducer,后续使用时可以对这个reducer调用createStore
/** 文件示例*/
import { combineReducers } from ''redux'';
import reducer from ''./reducer'';

const store = combineReducers({
    ...reducer
});
export default store;

下面是在组件中如何使用

在根组件中创建store

import state from ''./redux'';
import { Provider } from ''react-redux'';
import { createStore } from ''redux'';
const store = createStore(state); 

function App() {
    return (
        <Provider store={store}>
            <div className="App">
                <ComOne/>
                <ComTwo/>
            </div>
        </Provider>
    );
}

在组件中如何使用

import { connect } from ''react-redux'';
// 引入action
import { USER_CODE_ACTION } from ''../../redux/action/module1'';

function ComOne(props) {
    const [ count, setCount ] = useState(0);
    return (
        <div className="ComOne">
            <div>{props.userCode}</div>
            <Button type=''primary'' onClick={ ()=>{props.USER_CODE_ACTION(Math.random())}}>提交</Button>
        </div>
    );
}

// mapStateToProps方法:将state中的变量合成到组件的props中
const mapStateToProps = (state) => { 
    return { 
        userCode: state.USER_INFO.userCode
    }
};
// mapDispatchToProps方法:将action合成到组件的props中,在组件中可以直接props.USER_CODE_ACTION()进行调用
const mapDispatchToProps = {
    USER_CODE_ACTION 
};
/** 
* connect api
* 首先connect之所以会成功,是因为Provider组件, 在原应用组件上包裹一层,使原来整个应用成为Provider的子组件,它真正连接 Redux 和 React
*/
export default connect(mapStateToProps, mapDispatchToProps)(ComOne);

react 系列(五)在 React 中使用 Redux

react 系列(五)在 React 中使用 Redux

上一篇展示了 Redux 的基本使用,可以看到 Redux 非常简单易用,不限于 React,也可以在 Angular、Vue 等框架中使用,只要需要 Redux 的设计思想的地方,就可以使用它。
这篇主要讲解在 React 中使用 Redux,首先是安装。

安装 React Redux

yarn add redux
yarn add react-redux

有两个概念:
1. 容器组件(Container Components)
2. 展示组件(Presentational Components)

展示组件

  • 更关注数据展示,所以会写一些 DOM 嵌套和 CSS
  • 通常不依赖 Redux,直接从 props 中获取数据
  • 通常没有 state,偶尔会用 state 来保存一些展示状态,如 class 等
  • 交互也通过 props 回调发起,不直接发起 action

容器组件

  • 通常作为数据源,做数据分发工作
  • 依赖 Redux
  • 通过和 store 交互进行数据变更
  • 通过 react-redux 生成

在我们的项目中,一般来说,会编写很多展示组件,少量的容器组件来包裹这些展示组件。
接下来写一个简单的计数器应用,先来划分容器组件和展示组件。
计数器有三个按钮,加、减、重置;一个展示区。
由于按钮既要触发 action,又要负责展示,所以需要做成混合组件。
先来编写展示组件,就是显示一下当前计数。

import React from ''react'';
const Counter = ({
    count
}) => (
    <p>当前计数为:<span style={{color: ''red''}}>count</span></p>
)

export default Counter;

一般来说,容器组件就是通过 store.subscribe 传入回调,订阅 store 的变化,再去把值通过 props 传入各个组件中。
在 react-redux 中实现了 connect 方法,它生成一个高阶组件,就是前面提到的容器组件。这个方法做了性能优化避免不必要的重复渲染,建议使用该方法。

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

mapStateToProps 是一个 Function,用来监听 Redux Store 的变化,将 store 的值,映射为对应的 props 属性。

const mapStateToProps = ({count}) => {count};
// 或者
const mapStateToProps2 = (state) => {
    count: state.count
}

接下来生成一个容器组件。

import { connect } from ''react-redux'';

const ConnectCounter = connect(
  mapStateToProps
)(Counter);

export default ConnectCounter;

接下来是按钮组件,按钮组件既需要展示,又有数据交互,做成混合组件。
由于,需要 dispatch,所以需要给 connect 传入第二个参数。
mapDispatchToProps 可以是 Object 或者 Function。用来将 dispatch 映射到 props 上。

const mapDispatchToProps = dispatch => {
    return {
        plus: () => dispatch({
            type: ''PLUS''
        })
    }
}
// 或者结合上篇提到的bindActionCreators合成一个对象
function plus() {
  return {
    type: "PLUS"
  };
}

function minus() {
  return {
    type: "MINUS"
  };
}

const mapDispatchToProps2 = dispatch => {
    return bindActionCreators({ plus, minus }, dispatch)
}
import React from ''react'';

let Button = ({plus, minus}) => {
    return (
        <>
            <button onClick={plus}>{''plus''}</button>
            <button onClick={minus}>{''minus''}</button>
        </>
    )
};

Button = connect(()=>{}, mapDispatchToProps2)(Button);
export default Button;

最后,提供一个 Provider 用来提供全局 store。完整例子在这里 - codesandbox。 感谢阅读。

react+react-router+redux+react-redux构建一个简单应用

react+react-router+redux+react-redux构建一个简单应用

完整的demo代码:

https://gitee.com/dgx/demo-react

演示:

http://dgx.gitee.io/demo-react/build/index.html#/

一.基本知识

我们已经学习了react的语法使用,react-router和react的配合使用,redux通过react-redux的结合使用,下面我们要组合起来,开发一个简单的应用。

二.应用结构

index.html(我们的单页开发核心静态页面)

index.js(应用渲染首页面)

App.js(核心页面)

rootRedux.js(合并所有状态,对外接口)

indexRedux.js(根状态树)

 

page/(目录,存放路由页面组件)

page/login/LoginReactRedux.js(登录页面组件被react-redux封装,我们的首页显示页面,需要用户登录)

page/login/Login.js(登录页面组件)

page/login/LoginRedux.js(登录页面reducer)

我们不在创建action的页面,不会去分离出去,我们的业务只是demo使用

其他路由页面构建类似...(包含登录页面,主页面,关于我们,新闻中心四个页面作为演示)

 

tpl/(目录,存放公用组件,用于路由页面公用显示使用)

 

我们的ajax处理都会利用setTimeout去模拟,同样也不使用action可以用函数的中间件,因为应用非常简单

三.创建初始化应用

利用我们的create-react-app 项目名 来创建,执行下面创建我们的demo-react应用:

create-react-app demo-react

删除一些不要的东西,让我们的项目看起来尽可能简洁:

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

index.js

import React from ''react'';
import ReactDOM from ''react-dom'';
import App from ''./App'';
import registerServiceWorker from ''./registerServiceWorker'';

ReactDOM.render(<App />, document.getElementById(''root''));
registerServiceWorker();

App.js

import React, { Component } from ''react'';

class App extends Component {
  render() {
    return (
      <div className="App">
        demo
      </div>
    );
  }
}

export default App;

四.安装需要的依赖

我们的项目需要:

react-router:

npm install react-router

react-router-dom:(我们实际使用的路由模块)

npm install react-router-dom

redux:

npm install redux

react-redux:

npm install react-redux

等待完成...

我们的使用版本:

我们采用的react16.x和react-router4.x,不同的版本使用是有区别的,尤其路由使用上

五.创建目录结构和文件

page下存放我们路由使用的页面组件

六.创建路由页面和搭载路由

1.创建页面

这时候我们已经可以看到显示demo的页面,我们开始创建我们的页面:

login/Login.js

import React, { Component } from ''react'';

//=====组件=====

class Login extends Component {
	
	render() {
		return (
			<div>
				<h3>登录页面</h3>
				<div>
					用户名<input type="text" />
				</div>
				<div>
					密码<input type="text" />
				</div>
				<div>
					<button onClick={this.goLogin}>登录</button>
				</div>
			</div>
		);
	}
	
	goLogin(){
		alert("开始登录")
	}
	
	componentDidMount() {
	  	console.log("Login渲染完毕")
	}
	
}


export default Login

home/Home.js

import React, { Component } from ''react'';

//=====组件=====

class Home extends Component {
	
	render() {
		return (
			<div>
				<h3>主页</h3>
			</div>
		);
	}
	
	componentDidMount() {
	  	console.log("Home渲染完毕")
	}
	
}


export default Home

about/About.js

import React, { Component } from ''react'';

//=====组件=====

class About extends Component {
	
	render() {
		return (
			<div>
				<h3>关于我们</h3>
			</div>
		);
	}
	
	componentDidMount() {
	  	console.log("About渲染完毕")
	}
	
}


export default About

news/News.js

import React, { Component } from ''react'';

//=====组件=====

class News extends Component {
	
	constructor(props) {
		super(props);
		// 设置 initial state
		this.state = {
			list: [
				{id:1,title:"a",con:"caaaaaaaaaaaaaaaa"},
				{id:2,title:"b",con:"cbbbbbbbbbbb"},
				{id:3,title:"c",con:"cccccccccccccc"},
				{id:4,title:"d",con:"cddddddddddddd"},
				{id:5,title:"e",con:"ceeeeeeeeeeee"}
			]
		};
	}
	
	render() {
		return (
			<div>
				<h3>新闻页面</h3>
				<ul>
					{
						this.state.list.map(function(item,i){
							return <li key={item.id}>
								<a>{item.title}</a>
								<span>{item.con}</span>
							</li>						
						})	
					}
				</ul>
			</div>
		);
	}
	
	componentDidMount() {
	  	console.log("News渲染完毕")
	}
	
}


export default News

2.搭载路由

我们把页面创建完毕,在index.js配置路由:

index.js:

import React from ''react'';
import ReactDOM from ''react-dom'';
import {BrowserRouter as Router} from ''react-router-dom'';
import App from ''./App'';
import registerServiceWorker from ''./registerServiceWorker'';

ReactDOM.render(
	<Router>
		<App />
	</Router>	
, document.getElementById(''root''));
registerServiceWorker();

App.js完成我们路由和页面的使用:

App.js:

import React, { Component } from ''react'';
import {
  Route,
  Link
} from ''react-router-dom'';

import Login from ''./page/login/Login.js'';
import Home from ''./page/home/Home.js'';
import About from ''./page/about/About.js'';
import News from ''./page/news/News.js'';

class App extends Component {
  render() {
    return (
      <div className="App">
        	<ul>
				<li>
					<Link to="/">登录</Link>
				</li>
				<li>
					<Link to="/Home">主页</Link>
				</li>
				<li>
					<Link to="/About">关于我们</Link>
				</li>
				<li>
					<Link to="/News">新闻页面</Link>
				</li>
			</ul>
			<div>
				<Route exact path="/" component={Login}/>
				<Route exact path="/Home" component={Home}/>
				<Route path="/About" component={About}/>
				<Route path="/News" component={News}/>
			</div>
      </div>
    );
  }
}

export default App;

我们预览页面,就可以看到大概了:

七.redux和应用配合

创建我们的redux文件:

rootRedux.js(合并所有状态,对外接口):

import { combineReducers } from ''redux'';

//全局reducer
import isLogin from ''./indexRedux.js''
//子reducer


//合并reducer
var rootRedux = combineReducers({
	isLogin
})

export default rootRedux

indexRedux.js(根状态树)我们存放登录状态,默认是未登录:

//reducer

var isLogin=false;

function indexRedux(state = isLogin, action) {
	switch (action.type) {
		case "GO_LOGIN":
			//登录
			return true
		case "OUT_LOGIN":
			//退出登录
			return false
		default:
		  	return state
	}
}


export default indexRedux

index.js使用redux:

import React from ''react'';
import ReactDOM from ''react-dom'';
import {BrowserRouter as Router} from ''react-router-dom'';

//redux 和react-redux(关联react和redux)
import { createStore } from ''redux'';
import { Provider } from ''react-redux'';

//reducers 状态树state和逻辑操作
import rootRedux from ''./rootRedux.js''

import App from ''./App'';
import registerServiceWorker from ''./registerServiceWorker'';


//创建状态树和设置

//生成状态树对象
const store = createStore(rootRedux);

//start 状态树应用到全局 通过Provider
ReactDOM.render(
<Provider store={store}>
	<Router>
		<App />
	</Router>
</Provider>		
, document.getElementById(''root''));
registerServiceWorker();

我们为news创建reducer,把list放入在reducer中,

NewsRedux.js:

//reducer

var newsinit={
	list:[
		{id:1,title:"a",con:"caaaaaaaaaaaaaaaa"},
		{id:2,title:"b",con:"cbbbbbbbbbbb"},
		{id:3,title:"c",con:"cccccccccccccc"},
		{id:4,title:"d",con:"cddddddddddddd"},
		{id:5,title:"e",con:"ceeeeeeeeeeee"}
	]
};

function NewsRedux(state = newsinit, action) {
	switch (action.type) {
		case "SORT_REVERSE":
			//倒叙显示
			var arr=state.list;
			var arr2=[];
			for(var i=arr.length-1;i>=0;i--){
				arr2.push(arr[i])
			}
			return Object.assign({},state,{list:arr2})
		default:
		  	return state
	}
}


export default NewsRedux

News.js移除构造函数的json设置:

import React, { Component } from ''react'';

//=====组件=====

class News extends Component {
	
	constructor(props) {
		super(props);
	}
	
	render() {
		return (
			<div>
				<h3>新闻页面</h3>
				<ul>
					
				</ul>
			</div>
		);
	}
	
	componentDidMount() {
	  	console.log("News渲染完毕")
	}
	
}


export default News

rootRedux.js引入news的reducer:

import { combineReducers } from ''redux'';

//全局reducer
import isLogin from ''./indexRedux.js''
//子reducer
import NewsRedux from ''./page/news/NewsRedux.js''

//合并reducer
var rootRedux = combineReducers({
	isLogin,
	NewsRedux
})

export default rootRedux

八.利用react-redux链接react组件和redux

我们先以news的处理作为操作,首先用react-redux封装News.js组件:

创建NewsReactRedux.js:

import { connect } from ''react-redux'';

//=====引入组件=====
import News from ''./News.js''


//=====react-redux 封装组件=====

// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的?
function mapStateToProps(state) {
	return {
		list: state.NewsRedux.list
	};
}

// 哪些 action 创建函数是我们想要通过 props 获取的?
function mapDispatchToProps(dispatch) {
	return {
		SORTREVERSE:function(){
			dispatch({type:"SORT_REVERSE"})
		}
	};
}

//封装传递state和dispatch
var NewsReactRedux = connect(mapStateToProps,mapDispatchToProps)(News);

export default NewsReactRedux

我们把redux的状态数据和action全部发射给News组件,我们在里面使用即可:

News.js

import React, { Component } from ''react'';

//=====组件=====

class News extends Component {
	
	constructor(props) {
		super(props);
	}
	
	render() {
		return (
			<div>
				<h3>新闻页面</h3>
				<ul>
					{
						this.props.list.map(function(item,i){
							return <li key={item.id}>
								<a>{item.title}</a>
								<span>{item.con}</span>
							</li>
						})	
					}
				</ul>
				<button onClick={this.SORTREVERSE.bind(this)}>倒叙显示</button>
			</div>
		);
	}
	
	SORTREVERSE(){
		this.props.SORTREVERSE();
	}
	
	componentDidMount() {
	  	console.log("News渲染完毕")
	}
	
}


export default News

App.js使用封装后的组件News:

import React, { Component } from ''react'';
import {
  Route,
  Link
} from ''react-router-dom'';

import Login from ''./page/login/Login.js'';
import Home from ''./page/home/Home.js'';
import About from ''./page/about/About.js'';
import NewsReactRedux from ''./page/news/NewsReactRedux.js'';

class App extends Component {
  render() {
    return (
      <div className="App">
        	<ul>
				<li>
					<Link to="/">登录</Link>
				</li>
				<li>
					<Link to="/Home">主页</Link>
				</li>
				<li>
					<Link to="/About">关于我们</Link>
				</li>
				<li>
					<Link to="/News">新闻页面</Link>
				</li>
			</ul>
			<div>
				<Route exact path="/" component={Login}/>
				<Route exact path="/Home" component={Home}/>
				<Route path="/About" component={About}/>
				<Route path="/News" component={NewsReactRedux}/>
			</div>
      </div>
    );
  }
}

export default App;

九.登录处理

/地址就是我们的登录页面,我们点击登录跳转就可以了,不过我们会把用户的登录状态存放在indexRedux.js中,我们不把这个状态存如cookie类似的本地,所以 我们刷新页面退出即可,我们只是模拟的处理:

如果存放在了cookie我们要如何处理,这时候在进入网站我们可以调用一个方法去获取cookie的登录状态,不管是什么我们都会执行action把redux的登录状态改为这个!

因为Login.js要和redux结合,我们要修改登录状态,我们还要结合router去手动跳转到主页,

LoginReactRedux.js:

import { connect } from ''react-redux'';

//=====引入组件=====
import Login from ''./Login.js''


//=====react-redux 封装组件=====

// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的?
function mapStateToProps(state) {
	return {}
}

// 哪些 action 创建函数是我们想要通过 props 获取的?
function mapDispatchToProps(dispatch) {
	return {
		GOLOGIN:function(username,password,history){
			console.log("用户名"+username)
			console.log("密码"+password)
			setTimeout(function(){
				dispatch({type:"GO_LOGIN"})
				history.push("/Home")
			},1000)
			
		}
	};
}

//封装传递state和dispatch
var LoginReactRedux = connect(mapStateToProps,mapDispatchToProps)(Login);

export default LoginReactRedux

login.js

import React, { Component } from ''react'';

//=====组件=====

class Login extends Component {
	
	render() {
		return (
			<div>
				<h3>登录页面</h3>
				<div>
					用户名<input type="text" ref="username" />
				</div>
				<div>
					密码<input type="password" ref="password" />
				</div>
				<div>
					<button onClick={this.goLogin.bind(this)}>登录</button>
				</div>
			</div>
		);
	}
	
	goLogin(){
		this.props.GOLOGIN(this.refs.username.value,this.refs.password.value,this.props.history);
	}
	
	componentDidMount() {
	  	console.log("Login渲染完毕")
	}
	
}


export default Login

App.js:

import React, { Component } from ''react'';
import {
  Route,
  Link
} from ''react-router-dom'';

import LoginReactRedux from ''./page/login/LoginReactRedux.js'';
import Home from ''./page/home/Home.js'';
import About from ''./page/about/About.js'';
import NewsReactRedux from ''./page/news/NewsReactRedux.js'';

class App extends Component {
  render() {
    return (
      <div className="App">
        	<ul>
				<li>
					<Link to="/">登录</Link>
				</li>
				<li>
					<Link to="/Home">主页</Link>
				</li>
				<li>
					<Link to="/About">关于我们</Link>
				</li>
				<li>
					<Link to="/News">新闻页面</Link>
				</li>
			</ul>
			<div>
				<Route exact path="/" component={LoginReactRedux}/>
				<Route exact path="/Home" component={Home}/>
				<Route path="/About" component={About}/>
				<Route path="/News" component={NewsReactRedux}/>
			</div>
      </div>
    );
  }
}

export default App;

十.退出登录处理

我们在/Home加一个按钮就是退出按钮他和我们的登录处理相反:

HomeReactRedux.js:

import { connect } from ''react-redux'';

//=====引入组件=====
import Home from ''./Home.js''


//=====react-redux 封装组件=====

// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的?
function mapStateToProps(state) {
	return {}
}

// 哪些 action 创建函数是我们想要通过 props 获取的?
function mapDispatchToProps(dispatch) {
	return {
		OUTLOGIN:function(history){
			dispatch({type:"OUT_LOGIN"})
			history.push("/")
			
		}
	};
}

//封装传递state和dispatch
var HomeReactRedux = connect(mapStateToProps,mapDispatchToProps)(Home);

export default HomeReactRedux

Home.js:

import React, { Component } from ''react'';

//=====组件=====

class Home extends Component {
	
	render() {
		return (
			<div>
				<h3>主页</h3>
				<div>
					<button onClick={this.outLogin.bind(this)}>退出登录</button>
				</div>
			</div>
		);
	}

	outLogin(){
		this.props.OUTLOGIN(this.props.history);
	}
		
	componentDidMount() {
	  	console.log("Home渲染完毕")
	}
	
}


export default Home

十一.权限处理

1.显示级别

我们的网站在/地址是处在登录页面,这时候我们应该只有登录框,在进入主页之后会看到跳转链接,我们要获取我们的登录状态,还控制一些标签的显示:

我们在App.js存放了我们的导航,我们只需要对这个组件利用react-redux做一次封装,拿到状态,利用style去处理即可:

我们把导航提出到组件,并且react-redux做封装,在App.js使用

Nav.js:

import React, { Component } from ''react'';
import {
  Route,
  Link
} from ''react-router-dom'';


class Nav extends Component {
  render() {
    return (
		<ul style={{display:this.props.isLogin?"block":"none"}}>
			<li style={{display:this.props.isLogin?"none":"block"}}>
				<Link to="/">登录</Link>
			</li>
			<li>
				<Link to="/Home">主页</Link>
			</li>
			<li>
				<Link to="/About">关于我们</Link>
			</li>
			<li>
				<Link to="/News">新闻页面</Link>
			</li>
		</ul>
    );
  }
}

export default Nav;

NavReactRedux.js:

import { connect } from ''react-redux'';

//=====引入组件=====
import Nav from ''./Nav.js''


//=====react-redux 封装组件=====

// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的?
function mapStateToProps(state) {
	return {
		isLogin:state.isLogin
	}
}

// 哪些 action 创建函数是我们想要通过 props 获取的?
function mapDispatchToProps(dispatch) {
	return {};
}

//封装传递state和dispatch
var NavReactRedux = connect(mapStateToProps,mapDispatchToProps)(Nav);

export default NavReactRedux

App.js我们使用封装后导航:

import React, { Component } from ''react'';
import {
  Route,
  Link
} from ''react-router-dom'';
import NavReactRedux from ''./NavReactRedux.js'';
import LoginReactRedux from ''./page/login/LoginReactRedux.js'';
import HomeReactRedux from ''./page/home/HomeReactRedux.js'';
import About from ''./page/about/About.js'';
import NewsReactRedux from ''./page/news/NewsReactRedux.js'';

class App extends Component {
  render() {
    return (
      <div className="App">
        	<NavReactRedux />
			<div>
				<Route exact path="/" component={LoginReactRedux}/>
				<Route exact path="/Home" component={HomeReactRedux}/>
				<Route exact path="/About" component={About}/>
				<Route exact path="/News" component={NewsReactRedux}/>
			</div>
      </div>
    );
  }
}

export default App;

 

我们测试是没有问题的,我们在显示一级的权限做的差不多了!

 

2.逻辑级别

如果用户直接输入地址怎么办?所以我们在路由对应的页面都要加入登录状态的判断,然后处理是留在当前页面还是跳到登录页面:

我们登录后的页面只有三个,我们先对Home做一个处理,其他的类似:

HomeReactRedux.js:

import { connect } from ''react-redux'';

//=====引入组件=====
import Home from ''./Home.js''


//=====react-redux 封装组件=====

// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的?
function mapStateToProps(state) {
	return {
		isLogin:state.isLogin
	}
}

// 哪些 action 创建函数是我们想要通过 props 获取的?
function mapDispatchToProps(dispatch) {
	return {
		OUTLOGIN:function(history){
			dispatch({type:"OUT_LOGIN"})
			history.push("/")
			
		}
	};
}

//封装传递state和dispatch
var HomeReactRedux = connect(mapStateToProps,mapDispatchToProps)(Home);

export default HomeReactRedux

Home.js

import React, { Component } from ''react'';
import {Redirect} from ''react-router-dom'';

//=====组件=====

class Home extends Component {
	
	render() {
		if(this.props.isLogin==false){
			return <Redirect to="/" />
		}
		
		return (
			<div>
				<h3>主页</h3>
				<div>
					<button onClick={this.outLogin.bind(this)}>退出登录</button>
				</div>
			</div>
		);
	}

	outLogin(){
		this.props.OUTLOGIN(this.props.history);
	}
		
	componentDidMount() {
	  	console.log("Home渲染完毕")
	}
	
}


export default Home

其他路由页面同理!!!

十二.刷新问题

我们本地存储可以使用cookie还可以使用localstorage,我们刷新应用就获取localstorage对登录状态的设置,然后action即可!

不过不管是cookie还是localstorage如果用户浏览器的安全级别高就完蛋了,我们存放在这个2个里面哪一个都会遇到这个问题。

我们或许可以这样做,在刷新我们就向后台发送一个请求,这个请求会返回用户是否在登录中和返回用户的一些信息,根据状态我们用手动方法跳转链接。

十三.404

这个其实使用的就是router为我们提供的 Switch 组件:

import React, { Component } from ''react'';
import {
  Route,
  Link,
  Switch
} from ''react-router-dom'';
import NavReactRedux from ''./NavReactRedux.js'';
import LoginReactRedux from ''./page/login/LoginReactRedux.js'';
import HomeReactRedux from ''./page/home/HomeReactRedux.js'';
import About from ''./page/about/About.js'';
import NewsReactRedux from ''./page/news/NewsReactRedux.js'';
import NotFind from ''./page/notFind/NotFind.js'';

class App extends Component {
  render() {
    return (
      <div className="App">
        	<NavReactRedux />
			<div>
				<Switch>
					<Route exact path="/" component={LoginReactRedux}/>
					<Route exact path="/Home" component={HomeReactRedux}/>
					<Route exact path="/About" component={About}/>
					<Route exact path="/News" component={NewsReactRedux}/>		
					<Route component={NotFind}/>			
				</Switch> 
			</div>
      </div>
    );
  }
}

export default App;

十四.打包

我们执行下面命令:

npm run build

打包后文件index.html删除 /

我们打开index.html会出现问题,提示404,我们可以把路由处理改为:

HashRouter方式

index.js:

import React from ''react'';
import ReactDOM from ''react-dom'';
import {HashRouter as Router} from ''react-router-dom'';

//redux 和react-redux(关联react和redux)
import { createStore } from ''redux'';
import { Provider } from ''react-redux'';

//reducers 状态树state和逻辑操作
import rootRedux from ''./rootRedux.js''

import App from ''./App.js'';

import registerServiceWorker from ''./registerServiceWorker'';


//创建状态树和设置

//生成状态树对象
const store = createStore(rootRedux);

//start 状态树应用到全局 通过Provider
ReactDOM.render(
<Provider store={store}>
	<Router>
		<App />
	</Router>
</Provider>		
, document.getElementById(''root''));
registerServiceWorker();

(打包后文件index.html删除 /)

 

完整的demo代码:

https://gitee.com/dgx/demo-react

 

关于[译文&摘抄]在 React & Redux 中使用 AJAX 轮询react-redux-router的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于Flux、Redux到react-redux衍变发展之Redux解读、react redux react-redux使用方式(一)、react 系列(五)在 React 中使用 Redux、react+react-router+redux+react-redux构建一个简单应用的相关信息,请在本站寻找。

本文标签: