react + redux Router + node实践总结(redux+react-router)
redux:专注于状态管理的库
redux是什么:
- redux专注于状态管理,和react解耦(亦可与vue、angular等联合使用)
- 单一状态,单向数据流
- 核心概念:store, state, action, reducer
redux的功能:
- redux有一个保险箱(store),所有的状态,在这里都有记录(state)
- 需要改变的时候,需要告诉专员(dispatch)要干什么(action)
- 处理变化的人(reducer)拿到state和action,生成新的state,生成后使用新的state重新render页面
redux的正确使用方法
- 首先通过reducer新建store,随时通过store.getState获取状态
- 需要状态变更,store.dispatch(action)来修改状态
- reducer函数接受state和action,返回新的state,可以用store.subscribe监听每次修改
import { createStore } from 'redux'// 通过reducer修改state
// reducer根据旧state和action,生成新的state
function counter(state=0, action) {switch(action.type) {case 'add':return state + 1case 'reduce':return state - 1default:return 10}
}//使用reducer新建store
const store = createStore(counter)
// 定义监听器,监听store
function listenerStore() {// 获取所有的stateconst current = store.getState()console.log(`当前state为${current}`)
}
// 订阅监听器
store.subscribe(listenerStore)//派发事件,传递action
//listenerStore中会打印出11 10
store.dispatch({type: 'add'})
store.dispatch({type: 'reduce'})
redux如何和react结合使用(普通使用)
- 把store.dispatch方法传递给组件,内部可以调用修改状态
- Subscribe订阅render函数,每次修改都重新渲染
- Redux相关内容,移到单独的文件index.redux.js单独管理
更进一步:处理异步、调试工具、更优雅的和react结合 - Redux处理异步,需要redux-thunk插件(中间件)
- npm install redux-devtools-extension并且开启(调试工具)
- 使用react-redux优雅的连接react与redux
如何处理异步:Redux默认只处理同步,异步任务需要react-thunk中间件 - Npm install redux-thunk –save
- 使用applyMiddleware开启thunk中间件
- Action可以返回函数,使用dispatch提交action
调试:chrome扩展程序安装redux - 新建store的时候判断window.devToolsExtension
- 使用compose结合thunk和window.devToolsExtension
- 调试窗的redux选项卡,实时看到state
// index.redux.js文件,存放reducer
const Add = 'add'
const Reduce = 'reduce'// 通过reducer修改state
// reducer根据旧state和action,生成新的state
export function counter(state=0, action) {switch(action.type) {case Add:return state + 1case Reduce:return state - 1default:return 10}
}export function addFun() {return {type: Add}
}
export function reduceFun() {return {type: Reduce}
}export function asynAdd() {// 注意return的是一个箭头函数,dispatch参数return dispatch => {setTimeout(() => {dispatch(addFun())}, 2000)}
}// index.js文件,父组件
import React from 'react'
import ReactDom from 'react-dom'
// applyMiddleware用于开启react-thunk, 管理redux中间件
// compose结合thunk和调试工具,实时监控state
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import App from './app'
// 为解耦,应将addFun在此处引用,而不是放在app中,app中应该是个干干净净的组件
import { counter, addFun, reduceFun, asynAdd } from "./index.redux"// compose(开启thunk, 判断浏览器是否安装了redux调试工具安装则开启否则执行空函数)
const store = createStore(counter, compose(applyMiddleware(thunk),window.devToolsExtension ? window.devToolsExtension() : f => f
))function render() {ReactDom.render( ,document.getElementById('root'))
}
render()
//若不订阅render,state发生变化后不会重新渲染render
store.subscribe(render)// app.js, 子文件
import React from 'react'class App extends React.Component {// constructor(props) {// super(props)// }render() {const store = this.props.storeconst num = store.getState()const addFun = this.props.addFunconst reduceFun = this.props.reduceFunconst asynAdd = this.props.asynAddreturn (当前state为{num}
)}
}export default App
使用react-redux
- npm install react-redux –save
- 忘记subscribe,记住reducer, action和dispatch即可
- React-redux提供provider和connect两个接口来连接
- provider组件在应用最外层,传入store即可,只用一次
- Connect负责从外部获取组件需要的参数
- Connect可以用装饰器的方式来写
- Npm run eject弹出个性化配置
- npm install babel-plugin-transform-decorators-legacy –save-dev安装装饰器插件
- package.json里babel加上plugins配置
// 使用react-redux重写上面的组件
// index.redux.js不变
// index.js,父组件
import React from 'react'
import ReactDom from 'react-dom'
// applyMiddleware用于开启react-thunk, 管理redux中间件
// compose结合thunk和调试工具,实时监控state
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import App from './app'
import { counter } from "./index.redux"// compose(开启thunk, 判断浏览器是否安装了redux调试工具安装则开启否则执行空函数)
const store = createStore(counter, compose(applyMiddleware(thunk),window.devToolsExtension ? window.devToolsExtension() : f => f
))// react-redux管理, provider只使用一次
ReactDom.render(( ),document.getElementById('root')
)
// app.js,子组件
import React from 'react'
import { connect } from 'react-redux'
import { addFun, reduceFun, asynAdd } from "./index.redux";/* 正常写法
const mapStatetoProps = (state) => {return {num: state}
}
const actionCreators = {addFun, reduceFun, asynAdd}
// 装饰器的方式,connect先执行,再将App当作参数传进去
// connect(你要state什么属性放到props中,你要什么action放到props中并自动dispatch)
App = connect(mapStatetoProps, actionCreators)(App)
*/
// 使用babel-plugin-transform-decorators-legacy装饰器插件的写法,效果同上
// babel-plugin-transform-decorators-legacy插件需安装,并在package.json的babel中进行配置
@connect(state => ({num: state}),{addFun, reduceFun, asynAdd}
)
class App extends React.Component {render() {return (当前state为{this.props.num}
)}
}
export default App
React-router4:react路由库
- 4是全新的版本,和之前(2)版本不兼容,浏览器和RN均兼容
- React开发单页应用必备,践行路由即组件的概念
- 核心概念:动态路由、route、link、switch
- npm install react-router-dom –save 安装浏览器端的路由
- BrowserRouter,包裹整个应用,只使用一次(路由有BrowserRouter和HashRouter
- Router路由对应渲染的组件,可嵌套
- Link跳转专用
- url参数, Route组件参数可用冒号标识参数
- Redirect组件跳转
- Switch只渲染一个子Route组件
- 复杂redux应用,多个reducer, 用combineReducers合并
import { BrowserRouter, Route, Link, Redirect, Switch } from 'react-router-dom'
function Router2() {return <h2>Router2h2>
}
function Router3() {return <h2>Router3h2>
}function RouterParam(props) {return <h2>带参数的url, 当前router为:{props.match.params.location}h2>
}
// react-redux管理, provider只使用一次
// Link: 点击跳转到指定路由, Route: 路由对应渲染模板
ReactDom.render((<Provider store={store}><BrowserRouter><div><ul><li><Link to='/'>router1Link>li><li><Link to='/router2'>router2Link>li><li><Link to='/router3'>router3Link>li>ul>{/*switch只渲染匹配到的第一个路由*/}<Switch>{/*exact代表完全匹配,若省去,会按正则匹配,所有包含的/都会显示此路由下的模块内容*/}<Route path='/' exact component={App}>Route><Route path='/:location' component={RouterParam}>Route><Route path='/router2' component={Router2}>Route><Route path='/router3' component={Router3}>Route>Switch>{/*强制跳转至指定页面*/}<Redirect to='/router2'>Redirect>div>BrowserRouter>Provider>),document.getElementById('root')
)
redux+react-router实战
// index.js: 顶级父组件
import React from 'react'
import ReactDom from 'react-dom'
// applyMiddleware用于开启react-thunk, 管理redux中间件
// compose结合thunk和调试工具,实时监控state
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import { BrowserRouter, Route, Redirect, Switch } from 'react-router-dom'
import reducers from './reducers'
import Auth from './Auth'
import Dashbord from './dashbord'// compose(开启thunk, 判断浏览器是否安装了redux调试工具安装则开启否则执行空函数)
const store = createStore(reducers, compose(applyMiddleware(thunk),window.devToolsExtension ? window.devToolsExtension() : f => f
))// react-redux管理, provider只使用一次
// Link: 点击跳转到指定路由, Route: 路由对应渲染模板
ReactDom.render(({/*switch只渲染匹配到的第一个路由*/}{/*exact代表完全匹配,若省去,会按正则匹配,所有包含的/都会显示此路由下的模块内容*/}'/login' exact component={Auth}> '/dashbord' component={Dashbord}> {/*强制跳转至指定页面*/}'/dashbord'> ),document.getElementById('root')
)// Auth.redux.js, 登录状态管理,未登录用户不可查看dashbord页面
const Login = 'login'
const Logout = 'logout'export function auth(state={isAuth: false, user: 'lmh'}, action) {switch(action.type) {case Login:return {...state, isAuth: true}case Logout:return {...state, isAuth: false}default:return state}
}//action
export function login() {return {type: Login}
}
export function logout() {return {type: Logout}
}// reducers.js, 由于app.js模块与auth.js模块都有reducer状态管理,多处调用不方便,所以在此文件中使用combineReducers合并所有reducer
// combineReducers合并所有reducer, 并且返回
import { combineReducers } from 'redux'
import { counter} from "./index.redux";
import { auth } from "./auth.redux";export default combineReducers({counter, auth})// auth.js, 登录页面管理
import React from 'react'
import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom'
import { login } from "./auth.redux";@connect(state => state.auth,{login}
)class Auth extends React.Component {render() {return({this.props.isAuth ? '/dashbord/' /> : null}还未登录,请点击登录
)}
}export default Auth// dashbord.js, 导航页面管理
import React from 'react'
import { Link, Route, Redirect } from 'react-router-dom'
import { connect } from 'react-redux'
import App from './app'
import { logout } from "./auth.redux";function RouterParam(props) {return 当前路由为:{props.location.pathname}
}@connect(state => state.auth,{logout}
)class Dashbord extends React.Component {render() {const match = this.props.matchconst directToLogin = '/login'> const app = ({this.props.isAuth ? : null}- match.url}`}>router1
- match.url}/router2`}>router2
- match.url}/outer3`}>router3
match.url}`} exact component={App}> match.url}/router2`} component={RouterParam}> match.url}/outer3`} component={RouterParam}> )return this.props.isAuth ? app : directToLogin}
}export default Dashbord// index.redux.js模块同上
// app.js模块同上
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
