容器组件、UI组件和无状态组件
容器组件:处理数据
UI组件:处理视图
无状态组件 纯净的render
异步请求
componentDidMount中执行ajax请求
async componentDidMount(){ let {status, data:{data}} = await axios.get('/api/data.json') if (status === 200) { store.dispatch(initListAction(data)) } }
Redux-thunk中间件实现Ajax
npm install redux-thunk
配置
import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import rootReducer from './reducers/index'; // Note: this API requires redux@>=3.1.0 const store = createStore( rootReducer, applyMiddleware(thunk) );
因为我们使用了Redux devtool插件
配置修改为
import { createStore, compose, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import reducer from './reducer'; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose const store = createStore(reducer, composeEnhancers( applyMiddleware(thunk) )) export default store;
接下来修改Ajax请求
首先在createAction.js添加Ajax请求,使用thunk可以返回一个函数
export const getTodoList = () => { return async dispatch => { let {status, data:{data}} = await axios.get('/api/data.json') if (status === 200) { const action = initListAction(data) dispatch(action) } } }
在Component action会接收getTodoList函数,调用dispatch如果发现action不是对象而且是函数会自动执行,即getTodoList
const action = getTodoList() store.dispatch(action)
我们可以看出使用thunk是对dispatch的一次升级 dispatch不光可以接受对象还可以接受函数,如果接受函数会自动执行
Redux-saga中间件实现Ajax
npm install --save redux-saga
在index.js配置如下 引入redux-saga
import { createStore, compose, applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' import reducer from './reducer' import saga from './saga' const sagaMiddleware = createSagaMiddleware() const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose const store = createStore(reducer, composeEnhancers( applyMiddleware(sagaMiddleware) )) sagaMiddleware.run(saga) export default store
创建saga.js
function* mySaga() { } export default mySaga;
在组件Component组件store.dispatch(action) ,saga会通过takeEvery捕获,然后执行 generator
function* getInitList() { try { let {status, data:{data}} = yield axios.get('/api/data.json') if (status === 200) { const action = initListAction(data) yield put(action) } } catch (error) { } } function* mySaga() { yield takeEvery(types.GET_INIT_LIST, getInitList) } export default mySaga;
react-redux
npm install --save react-redux
首先在src->Index.js引入 然后把store挂载上去,让App其他Component都能使用store能力
import {Provider} from 'react-redux' import store from './store' const App = ( <Provider store={store}> <TodoList /> </Provider> )
在Component组件引入react-redux的connect 将store和Commponent连接起来
connect有2个参数
mapStateToprops 是state到Component的props的映射
mapDispatchToProps 定义了用户操作action,改变state的值
onst mapStateToprops = state => { return {} } const mapDispatchToProps = dispatch => { return {} } export default connect(mapStateToprops, mapDispatchToProps)(TodoList)
使用combineReducers拆分数据管理
修改store下reducer.js代码,拆分数据
import { combineReducers } from 'redux' import {reducer as headerReducer } from '../common/header/store' export default combineReducers({ header: headerReducer })
使用immutable和redux-immutable
非常安全的修改state中数据
配置
reducer.js
import { combineReducers } from 'redux-immutable'
const defaultState = fromJS({ city: '上海' })
常用的方法: 1:fromJS 把一个普通对象改变成一个immutable对象 2:set immutable对象的set方法,会结合之前immutable对象的值和设置的值,返回一个新的对象 使用如下: state.set('focused',true); 3:get : get() 、 getIn() 可以用来获取数据 如下使用: state.getIn(['header','list']) 等价于state.get('header').get('focused') 4:merge(浅合并,新数据与旧数据对比,旧数据中不存在的属性直接添加,就数据中已存在的属性用新数据中的覆盖) merge使用如下: state.merge({ list:action.data, totalPage:action.totalPage })
5:更新数组 update('chat', item=>item.concat(action.data))
使用styled-components
css in js类库,非常适合react
react-router-dom
核心代码片段
<BrowserRouter>
<div>
<Header/>
<Route path="/" exact component={Home}/>
<Route path="/detail/:id" exact component={Detail}/>
</div>
</BrowserRouter>
获得pathname
const {pathname} = this.props.location
页面跳转
this.props.history.push('/')
页面重定向
<Redirect from='/...' to='/...' />
react-loadable
异步加载组件
yarn add react-loadable
如何使用?比如我们在detail组件下新建loadable.js
loader 是加载当前目录下index.js 是异步加载语法
loading 进入detail组件需要时间,可以在页面显示临时内容
import Loadable from 'react-loadable'; import Loading from './my-loading-component'; const LoadableComponent = Loadable({ loader: () => import('./'), loading: Loading, }); export default class App extends React.Component { render() { return <LoadableComponent/>; } }
于是detail就是异步组件了
接下来修改detail路由
import Detail from './pages/detail/loadable.js'
此时会出现问题
TypeError: Cannot read property 'params' of undefined
因为加载的是异步组件,所以不能直接获取路由里面的内容,params
可以使用widthRouter方法,让detail有能力获得路由参数
export default connect(mapState, mapDispatch)(withRouter(Detail))
Fragment的使用
将多个元素集合在一起,不会产生新的DOM
Purerendermixin
这也是一个对shouldComponentUpdate优化方案