这篇六个点:1 Redux简介 2 中间件 3 异步流 4 Redux与路由 5 Redux与组件 6 应用实例。
4.1 Redux简介
4.1.1 Redux三大原则
1 单一数据源
在Redux思想里,一个应用只有唯一的数据源,好处是整个应用状态都保存在一个对象中,可以随时取出整个应用的状态进行持久化,这样的设计页尾服务端渲染提供了可能。
2 状态是只读的
在Redux中,并不会自己定义一个store,而是定义一个reducer,reducer根据当前触发的action对当前应用的状态进行更新
3 状态修改均由纯函数完成
在redux里,通过定义reducer来确定状态的修改,每一个reducer都是纯函数,接受一定的输入,必定得到一定的输出。
4.1.2 Redux四个部分
整体结构
import { createStore} from 'redux';
// reducer部分
const defaultState={
myValue:[],
inputValue:"默认值"
};
function reducer(state=defaultState,action) {
if (action.type==="add"){
const newState=JSON.parse(JSON.stringify(state));
newState.myValue.push(newState.inputValue);
console.log(newState);
return newState;
}
if (action.type==='change'){
const newState=JSON.parse(JSON.stringify(state));
newState.inputValue=action.value;
console.log(newState);
return newState;
}
return state;
}
// store 部分
const store=createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__&&window.__REDUX_DEVTOOLS_EXTENSION__()
);
class ReduxTest extends Component{
constructor(props){
super(props);
this.state=store.getState();
console.log(this.state);
this.handleClick=this.handleClick.bind(this);
this.handleChange=this.handleChange.bind(this);
// 状态改变就执行
store.subscribe(()=>{
this.setState(store.getState());
});
}
handleClick(){
const action={
type:'add'
};
store.dispatch(action);
}
handleChange(e){
const action={
type:'change',
value:e.target.value
};
store.dispatch(action);
}
render(){
return(
<div>
<input
value={this.state.inputValue||"HAHA"}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>改变</button>
</div>
)
}
}
- store
import { createStore} from 'redux';
const store=createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__&&window.__REDUX_DEVTOOLS_EXTENSION__()
);
Redux最核心的API,可以创建一个store对象,store又包含4个方法。
getState():获取store中当前的状态。
dispatch(action):分发一个action并返回这个action,这是唯一能改变store中数据的方式。
subscribe():注册一个监听者,当store发生变化时会被调用。
- action
唯一可以改变状态就是触发action,它是一个普通对象,用于描述已经发生的事件。type是必须的,其他可以自定义。
handleChange(e){
const action={
type:'change',
value:e.target.value
};
store.dispatch(action);
}
//实际这么用
function addTo(e){
return{
type:'change',
value:e.target.value
}
}
handleChange(e){
store.dispatch(addTo(e));
}
- reducer
通过dispatch发起action之后,最终是通过reducer指定如何state,reducer是用来修改状态的,要注意的是不要在reducer中直接修改state,推荐创建一个新的移入,重新通过getState返回新的状态,最终通过subscribe重新渲染。 - connect
略
4.2 中间件
https://www.jianshu.com/p/ae7b5a2f78ae
这是redux的数据流流程,当增加middleware后,就可以在途中对action进行截获。
。且由于业务场景的多样性,单纯的修改 dispatch 和 reduce 人显然不能满足大家的需要,因此对 redux middleware 的设计是可以自由组合,自由插拔的插件机制。也正是由于这个机制,我们在使用 middleware 时,我们可以通过串联不同的 middleware 来满足日常的开发,每一个 middleware 都可以处理一个相对独立的业务需求且相互串联:
如上图所示,派发给 redux Store 的 action 对象,会被 Store 上的多个中间件依次处理,如果把 action 和当前的 state 交给 reducer 处理的过程看做默认存在的中间件,那么其实所有的对 action 的处理都可以有中间件组成的。值得注意的是这些中间件会按照指定的顺序一次处理传入的 action,只有排在前面的中间件完成任务之后,后面的中间件才有机会继续处理 action,同样的,每个中间件都有自己的“熔断”处理,当它认为这个 action 不需要后面的中间件进行处理时,后面的中间件也就不能再对这个 action 进行处理了。
而不同的中间件之所以可以组合使用,是因为 Redux 要求所有的中间件必须提供统一的接口,每个中间件的尉氏县逻辑虽然不一样,但只要遵循统一的接口就能和redux以及其他的中间件对话了。
4.3 Redux异步流
- 1 redux-thunk中间件
redux-thunk是用于在redux中处理异步action的中间件,它 统一了异步和同步action的调用方式,使得异步过程放在action级别解决,避免异步操作对component的耦合。
使用这个插件,可以让action创建函数先不返回一个action对象,而是返回一个函数,函数传递两个参数(dispatch,getState)
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import { createStore,applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
// reducer部分
const defaultState={
myValue:[],
inputValue:"默认值",
num:3
};
function reducer(state=defaultState,action) {
if (action.type=='addValue'){
const newState=JSON.parse(JSON.stringify(state));
newState.num++;
console.log(newState);
return newState;
}
return state;
}
// store 部分
const store=createStore(
reducer,
applyMiddleware(thunk)
);
class Thunk extends Component{
constructor(props){
super(props);
this.state=store.getState();
// console.log(this.state);
this.handleClick=this.handleClick.bind(this);
this.handleChange=this.handleChange.bind(this);
// 状态改变就执行
store.subscribe(()=>{
this.setState(store.getState());
});
}
addIfOdd(){
return (dispatch,getState)=>{
console.log('111');
if (this.state.num%2===0){
return false
}else {
setTimeout(()=>{
dispatch({type:'addValue'})
},2000)
}
}
}
handleClick(){
store.dispatch(this.addIfOdd());
console.log("222")
}
handleChange(){}
render(){
return(
<div>
<input
value={this.state.num||"HAHA"}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>改变</button>
</div>
)
}
}
- Redux-saga 略
4.4 Redux与路由
http://www.ruanyifeng.com/blog/2016/05/react_router.html
import {BrowserRouter,Route} from 'react-router-dom';
//要渲染组件内的render
render() {
return (
<div>
<input
value={this.state.num || "HAHA"}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>改变</button>
<BrowserRouter>
<Route path='/' exact render={() => <div>首页</div>}/>
<Route path='/detail' exact render={() => <div>详情页</div>}/>
</BrowserRouter>
</div>
)
}
ReactDOM.render(
<Thunk/>,
document.getElementById('root'),
function () {
console.log("加载完毕");
}
);
4.5 Redux与组件
4.5.1 容器型组件
4.5.2 展示型组件
4.5.3 Redux中的组件
4.6 应用实例
mkdir my-blog
cd my-blog
G:\react\my-blog> npm install --save react react-dom redux react-router react-redux react-router-redux whatwg-fetch
https://www.jianshu.com/p/6a45e2dfc9d9/
app.js:整个应用的入口
views文件夹:页面的入口文件,常在路由中引用
components文件夹:下面对应页面的文件夹,里面一堆组件
containers :配置文件
layouts:公用的组件和样式。如菜单,header等
redux:Redux store相关的配置
routes:路由相关的配置
-views/Home.js
import React,{Component} from 'react';
class Home extends Component {
constructor(props){
super(props);
};
render(){
return(
<h1>Home</h1>
)
}
}
export default Home;
- views/Detail.js
import React,{Component} from 'react';
class Detail extends Component {
constructor(props){
super(props);
};
render(){
return(
<h1>Detail</h1>
)
}
}
export default Detail;
- route/index.js
import React from 'react';
import {BrowserRouter,Route} from 'react-router-dom';
import Home from '../views/Home';
import Detail from '../views/Detail';
const routes=(
<BrowserRouter>
<Route path='/' component={Home}/>
<Route path='/detail' component={Detail}/>
</BrowserRouter>
);
export default routes;
- app.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import {BrowserRouter,Route} from 'react-router-dom';
import routes from './routes';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a className="App-link" href="https://reactjs.org"
target="_blank" rel="noopener noreferrer"
>
Learn React
</a>
</header>
{routes}
</div>
);
}
export default App;
- index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
serviceWorker.unregister();
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
添加公用的组件
lauouts/Nav.js
import React,{Component} from 'react';
import {BrowserRouter as Router, Link} from 'react-router-dom';
class Nav extends Component {
constructor(props){
super(props);
};
render(){
return(
<nav>
<Link to='/'>首页</Link>
</nav>
)
}
}
export default Nav;
lauouts/Frame.js
import React,{Component} from 'react';
import {Link} from 'react-router-dom';
import Nav from './Nav';
class Frame extends Component {
constructor(props){
super(props);
};
render(){
return(
<div>
<Nav/>
</div>
)
}
}
export default Frame;
修改routes/index.js
import React from 'react';
import {BrowserRouter,Route} from 'react-router-dom';
import Home from '../views/Home';
import Detail from '../views/Detail';
import Frame from '../layouts/Frame'
const routes=(
<BrowserRouter>
<Frame/>
<Route path='/' component={Home}/>
<Route path='/detail' component={Detail}/>
</BrowserRouter>
);
export default routes;
继续修改App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import {BrowserRouter,Route} from 'react-router-dom';
import routes from './routes';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a className="App-link" href="https://reactjs.org"
target="_blank" rel="noopener noreferrer"
>
Learn React
</a>
</header>
{routes}
</div>
);
}
export default App;
成功实现路由
准备首页的数据
P242 后面看不懂 略