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 + 1
        case 'reduce':
            return state - 1
        default:
            return 10
    }
}

//使用reducer新建store
const store = createStore(counter)
// 定义监听器,监听store
function listenerStore() {
    // 获取所有的state
    const 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 + 1
        case Reduce:
            return state - 1
        default:
            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(
        <App
            store={store}
            addFun={addFun}
            reduceFun={reduceFun}
            asynAdd={asynAdd} />,
        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.store
        const num = store.getState()
        const addFun = this.props.addFun
        const reduceFun = this.props.reduceFun
        const asynAdd = this.props.asynAdd
        return (
            <div>
                <h1>当前state为{num}</h1>
                <button onClick={() => store.dispatch(addFun())}>增加state</button>
                <button onClick={() => store.dispatch(reduceFun())}>减少state</button>
                <button onClick={() => store.dispatch(asynAdd())}>异步增加state</button>
            </div>
        )
    }
}

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(
    (<Provider store={store}>
        <App />
    </Provider>),
    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 (
            <div>
                <h1>当前state为{this.props.num}</h1>
                <button onClick={this.props.addFun}>增加state</button>
                <button onClick={this.props.reduceFun}>减少state</button>
                <button onClick={this.props.asynAdd}>异步增加state</button>
            </div>
        )
    }
}
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>Router2</h2>
}
function Router3() {
    return <h2>Router3</h2>
}

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='/'>router1</Link></li>
                    <li><Link to='/router2'>router2</Link></li>
                    <li><Link to='/router3'>router3</Link></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(
    (<Provider store={store}>
        <BrowserRouter>
            {/*switch只渲染匹配到的第一个路由*/}
            <Switch>
                {/*exact代表完全匹配,若省去,会按正则匹配,所有包含的/都会显示此路由下的模块内容*/}
                <Route path='/login' exact component={Auth}></Route>
                <Route path='/dashbord' component={Dashbord}></Route>
                {/*强制跳转至指定页面*/}
                <Redirect to='/dashbord'></Redirect>
            </Switch>
        </BrowserRouter>
    </Provider>),
    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(
            <div>
                {this.props.isAuth ? <Redirect to='/dashbord/' /> : null}
                <h3>还未登录,请点击登录</h3>
                <button onClick={this.props.login}>登录</button>
            </div>
        )
    }
}

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 <h2>当前路由为:{props.location.pathname}</h2>
}

@connect(
    state => state.auth,
    {logout}
)

class Dashbord extends React.Component {
    render() {
        const match = this.props.match
        const directToLogin = <Redirect to='/login'></Redirect>
        const app = (
            <div>
                {this.props.isAuth ? <button onClick={this.props.logout}>注销</button> : null}
                <ul>
                    <li><Link to={`${match.url}`}>router1</Link></li>
                    <li><Link to={`${match.url}/router2`}>router2</Link></li>
                    <li><Link to={`${match.url}/outer3`}>router3</Link></li>
                </ul>
                <Route path={`${match.url}`} exact component={App}></Route>
                <Route path={`${match.url}/router2`} component={RouterParam}></Route>
                <Route path={`${match.url}/outer3`} component={RouterParam}></Route>
            </div>
        )
        return this.props.isAuth ? app : directToLogin
    }
}

export default Dashbord

// index.redux.js模块同上
// app.js模块同上

猜你喜欢

转载自blog.csdn.net/qq_40352733/article/details/80795649