React,React-Dom,Redux,React-Redux,React-Router基本依赖就不多说了
- React-Router-Redux
使用Redux来管理应用程序状态,使用React Router进行路由管理。但是两个库不协调,当你重播actions时React Router不会在页面之间导航。它控制应用的state的一个很重要的部分:URL。这个库帮助你保持这个状态与你的Redux存储同步。
注意:使用redux和react router时,这个库不是必须的。你可以不用任何其它的库只使用它们两个。如果你关心记录,持续,重播,用户行为和时光机,则它是有用的。如果你不在乎这些功能,那就直接使用Redux 和react router吧。
// 三步走
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux'
import { Router, Route, browserHistory } from 'react-router'
// 第一步:引入react-router-redux
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'
import reducers from '<project-path>/reducers'
// 第二步:添加自身的reducer到redux的createStore中(以routing为key)
const store = createStore(
combineReducers({
...reducers,
routing: routerReducer
})
)
// 第三步、创建一个增强版的history对象(能让store和导航事件保持同步)
const history = syncHistoryWithStore(browserHistory, store)
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<Route path="foo" component={Foo}/>
<Route path="bar" component={Bar}/>
</Route>
</Router>
</Provider>,
document.getElementById('mount')
)
- Redux-Actions
提供更为便捷的reducer创建方法,主要提供了API:handleActions
// 普通方式创建reducer
const defaultState = 10
const reducer = (state = defaultState, action) => {
switch (action.type) {
case Constants.INCREASE:
return state + 1
case Constants.DECREASE:
return state - 1
default:
return state
}
}
export default reducer
// 使用handleActions创建reducer
// 语法:handleActions({actionCreator},initialState)
import { handleActions } from 'redux-actions'
import { LOGIN_SUCCEEDED, LOGIN_FAILED, LOGOUT_SUCCEEDED, LOGOUT_FAILED} from '../constants/login'
export default handleActions(
{
[LOGIN_SUCCEEDED](state, action) {
return { ...state, hasLogin: true }
},
[LOGIN_FAILED](state, action) {
alert(action.message)
return state
},
[LOGOUT_SUCCEEDED](state, action) {
return { ...state, hasLogin: false }
},
[LOGOUT_FAILED](state, action) {
alert(action.message)
return state
},
},
{
hasLogin: false,
},
)
- Reselect
Reselect 库可以创建可记忆的(Memoized)、可组合的 selector 函数。Reselect selectors 可以用来高效地计算 Redux store 里的衍生数据。主要用在mapStateToProps中。
// 不使用reselect的通常写法:
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
export default VisibleTodoList
上面的示例中,mapStateToProps 调用了 getVisibleTodos 来计算 todos。运行没问题,但有一个缺点:每当组件更新时都会重新计算 todos。如果 state tree 非常大,或者计算量非常大,每次更新都重新计算可能会带来性能问题。Reselect 能帮你省去这些没必要的重新计算
// 使用reselect版本,三步走:
// 第一步:引入createSelector函数
import { createSelector } from 'reselect'
// 第二步:创建普通无记忆selector(即不涉及计算操作,仅是取值操作)
const getVisibilityFilter = (state) => state.visibilityFilter
const getTodos = (state) => state.todos
// 第三步:创建可记忆selector
export const getVisibleTodos = createSelector(
[ getVisibilityFilter, getTodos ],
(visibilityFilter, todos) => {
switch (visibilityFilter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
)
如果你在使用 React Redux,你可以在 mapStateToProps() 中当正常函数来调用 selectors
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
import { getVisibleTodos } from '../selectors'
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
export default VisibleTodoList
更多操作,请参看文档
- Redux-Saga
1、redux-saga 是一个用于管理 Redux 应用异步操作的中间件(又称异步 action)。 redux-saga 通过创建 Sagas 将所有的异步操作逻辑收集在一个地方集中处理,可以用来代替 redux-thunk 中间件。
2、Sagas 监听发起的action,然后决定基于这个 action来做什么:是发起一个异步调用(比如一个 fetch 请求),还是发起其他的action到Store,甚至是调用其他的 Sagas
// sagas.js基本套路:(put,call等effect定义异步操作,takeEvery或者takeLatest调用异步操作,一般一个action对应三个type,put,call等占用成功和失败,takeEvery占用请求开始)
import { put, call, take,fork } from 'redux-saga/effects';
import { takeEvery, takeLatest } from 'redux-saga'
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
function* incrementAsync() {
// 延迟 1s 再执行 + 1操作
yield call(delay, 1000);
yield put({ type: 'INCREMENT' });
}
export default function* rootSaga() {
// while(true){
// yield take('INCREMENT_ASYNC');
// yield fork(incrementAsync);
// }
// 下面的写法与上面的写法上等效
yield* takeEvery("INCREMENT_ASYNC", incrementAsync)
}
整合redux的用法:
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga'
import rootSaga from './sagas'
import Counter from './Counter'
import rootReducer from './reducers'
const sagaMiddleware = createSagaMiddleware()
let middlewares = []
middlewares.push(sagaMiddleware)
const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore)
const store = createStoreWithMiddleware(rootReducer)
sagaMiddleware.run(rootSaga)
const action = type => store.dispatch({ type })
function render() {
ReactDOM.render(
<Counter
value={store.getState()}
onIncrement={() => action('INCREMENT')}
onDecrement={() => action('DECREMENT')}
onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
document.getElementById('root')
)
}
render()
store.subscribe(render)
更多用法参考文档
- classnames
A simple javascript utility for conditionally joining classNames together
一个简单的JS工具(可根据状态决定cssName,即动态的cssName)
// 不使用 classnames时:
var Button = React.createClass({
render () {
var btnClass = 'btn';
//根据点击的state来控制css
if (this.state.isPressed) btnClass += ' btn-pressed';
else if (this.state.isHovered) btnClass += ' btn-over';
return <button className={btnClass}>{this.props.label}</button>;
}
});
// 使用classnames时(用法1):
var classNames = require('classnames');
var Button = React.createClass({
render () {
var btnClass = classNames({
'btn': true,
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
return <button className={btnClass}>{this.props.label}</button>;
}
});
// 用法2:与 [css-modules](https://github.com/css-modules/css-modules)
配合使用,通过用bind方法可返回样式对象的key
import { Component } from 'react';
import classNames from 'classnames/bind';
import styles from './submit-button.css';
let cx = classNames.bind(styles);
export default class SubmitButton extends Component {
render () {
let text = this.props.store.submissionInProgress ? 'Processing...' : 'Submit';//text根据状态来动态加载
let className = cx({
base: true,
inProgress: this.props.store.submissionInProgress,//样式的动态加载
error: this.props.store.errorOccurred,
disabled: this.props.form.valid,
});
return <button className={className}>{text}</button>;
}
};
网友评论