本文转载自:众成翻译
译者:iOSDevLog
连接:http://www.zcfy.cc/article/3811
原文:https://www.fullstackreact.com/30-days-of-react/day-19/css
随着咱们了解了flux和Redux的知识,让咱们将Redux整合到咱们的应用中,并经过链接的应用。react
昨天, 咱们讨论了流量模式的缘由, 它是什么, 咱们有不一样的选择, 以及介绍了Redux。npm
今天, 咱们将回到代码和添加Redux在咱们的应用。如今咱们正在用它构建的应用是简单的, 这只会显示咱们最后一次获取当前时间的页面。为了简单起见, 咱们不会调用远程服务器, 只需使用 JavaScript 的Date
对象。redux
第一件事, 咱们使用redux将不得不安装redux库。咱们可使用npm
包管理器来安装redux
。在咱们之前构建的应用的根目录中, 让咱们运行npm install
命令来安装redux:promise
npm install --save redux
想使用redux, 咱们还须要安装另外一个包, react-redux
, 帮助咱们把react
和 redux
绑在一块儿。浏览器
npm install --save react-redux
接下来的工做, 咱们须要作的是在咱们的应用内创建redux。咱们须要执行如下操做才能设置它:服务器
定义归约react-router
建立Storedom
建立动做创造者ide
将Store与咱们的React意见联系起来
得益
第5步没有promises, 但它会更好, 嗯?
咱们少许地讨论术语,(让咱们的手指移动是更重要的)。咱们将只是稍微调整咱们的应用(烦人, 我知道, 但这是最后一次), 因此咱们为了提供数据经过咱们的应用能够建立一个包装组件。
完成后, 咱们的应用树将具备如下形状:
[Root] -> [App] -> [Router/Routes] -> [Component]
废话少说, 让咱们将咱们的 src/App.js
移动到src/containers
目录中, 咱们须要同时更新来自咱们的导入的一些路径。咱们将使用几天前讨论的React路由材料。
咱们将在 <Switch />
语句中包含几条路由, 以确保一次只显示一个。
import React from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' // We'll load our views from the `src/views` // directory import Home from './views/Home/Home'; import About from './views/About/About'; const App = props => { return ( <Router> <Switch> <Route path="/about" component={About} /> <Route path="*" component={Home} /> </Switch> </Router> ) } export default App;
此外, 咱们将须要建立一个新的容器, 咱们将调用Root
, 这将包装咱们的整个 <App />
组件, 并使可用的其他应用。让咱们建立src/containers/Root.js
文件:
`touch src/containers/Root.js`
目前, 咱们将在这里使用一个占位符组件, 但咱们将在讨论存储时替换此内容。如今, 让咱们导出 _一些东西_:
import React from 'react'; import App from './App'; const Root = (props) => { return ( <App /> ); } export default Root;
最后, 让咱们更新的路由, 咱们渲染咱们的应用在src/index.js
文件使用咱们的新的Root
容器, 而不是它之前使用的App
。
import React from 'react'; import ReactDOM from 'react-dom'; import Root from './containers/Root'; import './index.css'; ReactDOM.render( <Root />, document.getElementById('root') );
如今有了一个坚实的应用结构就位, 咱们能够开始添加Redux。咱们将采起的步骤以配合一些咱们将创建的大多数应用的Redux结构一般都是相同的。咱们须要:
写一个根归约器
写 actionCreators
配置存储、rootReducer和应用
将视图链接到 actionCreators
咱们故意保持这个高层次的介绍短一些, 因此坚持一下, 这一切都将在短时间内变得更有意义。
让咱们设置容许咱们添加Redux的结构。咱们将在src/redux
目录中完成几乎全部的工做。让咱们建立该目录。
mkdir -p src/redux touch src/redux/configureStore.js touch src/redux/reducers.js
首先咱们先建立归约器。虽然它听起来很复杂, 可是归约器其实是至关直接的, 有必定的经验。归约器仅是 字面 函数。它的惟一责任是返回一个 next 状态的表示。
在Redux模式, 不像flux, 咱们只在 整个 应用处理 一个 全局存储的。这使得事情变得更容易处理, 由于有一个地方的数据, 咱们的应用生活。
root 归约器函数负责返回应用当前全局状态的表示形式。当咱们在存储上发送操做时, 将使用应用的当前状态和致使状态更新的操做来调用此归约器函数。
让咱们在一个文件中创建咱们的根归约器在src/redux/reducers.js
.。
// Initial (starting) state const initialState = { currentTime: new Date().toString() } // Our root reducer starts with the initial state // and must return a representation of the next state const rootReducer = (state = initialState, action) => { return state; } export default rootReducer
I在函数中, 咱们将第一个参数定义为初始状态 (第一次运行时, 不带参数调用rootReducer
, 所以它老是在第一次运行时返回 initialState
)。
这就是如今的 rootReducer。就像如今这样, state的价值老是和 initialState 同样。在咱们的例子中, 这意味着咱们的数据树有一个单一的currentTime
键。
这里的第二个参数是从存储区发送的操做。咱们很快就会回来。如今,让咱们来看看动做。
最起码, 一个动做 必须 包括一个type
键。type
键能够是咱们想要的任何值, 但它必须存在。例如, 在咱们的应用中, 咱们将偶尔发送一项操做, 咱们想告诉存储获取 最新 当前时间。咱们能够将此操做称为FETCH_NEW_TIME
的字符串值。
咱们可能从咱们的存储发送的处理此更新的操做以下:
{ type: 'FETCH_NEW_TIME' }
正如咱们将经过键入这个字符串不少, 咱们但愿避免可能的拼写错误的地方, 它是常见的建立一个types.js
文件, 将操做类型导出为常量。让咱们遵循这个约定, 建立一个src/redux/types.js
文件:
`export const FETCH_NEW_TIME = 'FETCH_NEW_TIME';`
咱们将从types.js
文件中引用它, 而不是使用'FETCH_NEW_TIME' 的硬编码字符串调用该操做:
import * as types from './types'; { type: types.FETCH_NEW_TIME, }
当咱们想发送数据与咱们的动做, 咱们能够添加任何键, 咱们想咱们的动做。咱们一般会看到这个所谓的payload 有效载荷
, 但它能够被称为任何东西。这是一个公约, 调用附加信息的payload
。
咱们的FETCH_NEW_TIME
动做将发送一个有效载荷与新的当前时间。由于咱们想发送一个 序列化 值与咱们的动做, 咱们将发送新的当前时间的字符串值。
{ type: types.FETCH_NEW_TIME, payload: new Date().toString() // Any serializable value }
回到咱们的归约器, 咱们能够检查的动做类型, 并采起适当的步骤, 建立下一个状态。在咱们的状况下, 咱们只储存payload
。若是操做的type
是FETCH_NEW_TIME
, 咱们将返回新的 currentTime (从咱们的操做有效负载) 和其余状态 (使用 ES6 扩展语法):
export const rootReducer = (state = initialState, action) => { switch(action.type) { case types.FETCH_NEW_TIME: return { ...state, currentTime: action.payload} default: return state; } }
请记住, 归约 必须 返回一个状态, 因此在默认状况下, _最起码_确保返回当前状态。
保持轻型化
因为归约器的功能每次调度一个动做, 咱们要确保这些功能是尽量简单和快速。咱们不但愿他们形成任何反作用或有太多的延迟。
咱们将处理动做创造者中归约器的反作用。
在咱们看动做创造者 (以及为何咱们称他们为动做创造者) 以前, 让咱们把咱们的存储与咱们的应用挂钩。
咱们将使用 react-redux
包将咱们的视图链接到咱们的redux存储。让咱们确保使用npm
安装此包:
npm install --save react-redux
react-redux
包输出一个名为 Provider
的组件。Provider
组件使存储可使用咱们的应用中的全部容器组件, 而无需咱们每次都须要手动传递它。
Provider
组件指望一个store
的属性, 它指望是一个有效的redux存储, 因此咱们的应用没有错误运行以前,咱们须要完成一个configureStore
功能,。如今, 让咱们在应用中链接Provider
组件。咱们将经过更新咱们之前建立的包装Root
组件来使用Provider
组件来完成此项。
import { Provider } from 'react-redux'; // ... const Root = (props) => { // ... return ( <Provider store={store}> <App /> </Provider> ); }
请注意, 咱们正在将store
值发送到Provider
组件, 但咱们尚未建立该存储。如今咱们来解决这个问题。
为了建立一个存储, 咱们将使用新的src/redux/configureStore.js
来导出将负责建立存储的函数。
咱们如何建立一个存储?
redux包输出一个叫作
createStore 的函数, 它将为咱们建立实际的存储区, 所以, 让咱们打开
src/redux/configureStore.js 文件并导出一个称为
configureStore()的函数 (咱们将很快定义), 并导入
createStore` 帮助器:
import {createStore} from 'redux'; // ... export const configureStore = () => { // ... } // ... export default configureStore;
实际上咱们在咱们的存储尚未返回任何东西, 因此让咱们实际建立的redux
存储使用 createStore
的功能, 咱们从redux导入:
import {createStore} from 'redux'; export const configureStore = () => { const store = createStore(); return store; } export default configureStore;
若是咱们在浏览器中加载咱们的页面, 咱们将看到咱们有一个巨大的错误: 没有页面获得渲染。
redux给咱们的错误告诉咱们, 咱们存储里没有归约器。若是没有归约器, 它将不知道如何处理动做或如何建立状态等。为了越过这个错误, 咱们须要参考咱们建立的 rootReducer。
createStore
函数要求咱们将 rootReducer 做为第一个参数来传递。它还但愿将初始状态做为第二个参数传递。咱们将从咱们建立的reducers.js
文件中导入这两个值。
import { rootReducer, initialState } from './reducers' // ... export const configureStore = () => { const store = createStore( rootReducer, // root reducer initialState, // our initialState ); return store; }
如今, 让咱们经过调用 configureStore()
函数建立store
的实例来更新咱们的Root.js
文件。
const Root = (props) => { const store = configureStore(); return ( <Provider store={store}> <App /> </Provider> ); }
咱们的应用中的一切都设置为使用redux, 而无需 太 多开销。redux
提供的一个更方便的方法是使用react-redux
包导出的connect()
函数, 将状态树的片段 绑定 到不一样的组件。
connect()
函数返回一个函数, 它指望第一参数是一个组件。这一般叫作高阶组件。
connect()
函数要求咱们在函数中至少传递一个参数 (但一般咱们会传递两个)。它所指望的第一个参数是一个函数, 它将被称为state
并指望一个返回的对象将数据链接到视图。让咱们看看咱们是否能在代码中揭开这种行为的神秘面纱。
咱们将这个函数称为mapStateToProps
函数。由于它的责任是将状态映射到与组件的原始props
合并的对象。
让咱们在src/views/Home.js
中建立 home 视图, 并使用此connect()
函数来绑定currentTime
在咱们的状态树中的值。
import { connect } from 'react-redux'; // ... const mapStateToProps = state => { return { currentTime: state.currentTime } } export default connect( mapStateToProps )(Home);
此connect()
函数 自动 将函数的第一个参数中的任何键传递为Home
组件的props
。
在咱们的演示案例中, Home
组件中的currentTime
属性将映射到currentTime
中的状态树键。让咱们更新 Home
组件, 以显示currentTime
中的值:
const Home = (props) => { return ( <div className="home"> <h1>Welcome home!</h1> <p>Current time: {props.currentTime}</p> </div> ); }
虽然这个演示不是颇有趣, 它代表咱们有咱们的Redux
应用创建了咱们的 data
致力于全局状态和咱们的视图组件映射数据。
明天, 咱们将开始经过操做建立者来触发咱们的全局状态的更新, 并经过将多个redux模块组合在一块儿来工做。