您好,登錄后才能下訂單哦!
小編給大家分享一下如何搭建一個react項目,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
1、npm init 生成 package.json 文件.
2、安裝各種需要的依賴:
npm install
--save react - 安裝React.
npm install
--save react-dom 安裝React Dom,這個包是用來處理virtual DOM。這里提一下用React Native的話,這里就是安裝react-native。
npm install
--save-dev webpack - 安裝Webpack, 現在最流行的模塊打包工具.
npm install
--save-dev webpack-dev-server - webpack官網出的一個小型express服務器,主要特性是支持熱加載.
npm install
--save-dev babel-core - 安裝Babel, 可以把ES6轉換為ES5,注意Babel最新的V6版本分為babel-cli和babel-core兩個模塊,這里只需要用babel-cor即可。
安裝其他的babel依賴(babel真心是一個全家桶,具體的介紹去官網看吧..我后面再總結,這里反正全裝上就是了):
npm install
--save babel-polyfill - Babel includes a polyfill that includes a custom regenerator runtime and core.js. This will emulate a full ES6 environment
npm install
--save-dev babel-loader - webpack中需要用到的loader.
npm install
--save babel-runtime - Babel transform runtime 插件的依賴.
npm install
--save-dev babel-plugin-transform-runtime - Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals.
npm install
--save-dev babel-preset-es2015 - Babel preset for all es2015 plugins.
npm install
--save-dev babel-preset-react - Strip flow types and transform JSX into createElement calls.
npm install
--save-dev babel-preset-stage-2 - All you need to use stage 2 (and greater) plugins (experimental javascript).
3、打開 package.json 然后添加下面的scripts:
"scripts": { "start": "webpack-dev-server --hot --inline --colors --content-base ./build", "build": "webpack --progress --colors" }
命令行輸入 npm start 將要啟動webpack dev server.
命令行輸入 npm build 將會進行生產環境打包.
4、啟動webpack
Webpack是我們的打包工具,在我們的開發環境中具體很重要的作用,具有很多非常便捷的特性,尤其是熱加載hot reloading. webpack.config.js 是如下所示的webpack的配置文件. 隨著app的不斷變化,配置文件也會不斷的更新,這里我們就用默認的webpack.config.js來命名這個配置文件,假如你用別的名字比如webpack.config.prod.js那么上面的腳本build就需要相應的改變指定相應的配置文件名字:"build": "webpack webpack.config.prod.js --progress --colors"
var webpack = require('webpack'); module.exports = { entry: './src/app.js', output: { path: __dirname + '/build', filename: "bundle.js" }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { plugins: ['transform-runtime'], presets: ['es2015', 'react', 'stage-2'] } }, { test: /\.css$/, loader: "style-loader!css-loader" }] } };
OK,我們項目的基本配置終于完成了,是時候開始寫Reac代碼了.
React 基礎 - 建立你的第一個Component
在上面的項目的基本配置基礎上,我們開始書寫React的第一個組件來熟悉React的寫法與組件思想。
首先我們在項目根目錄中新建一個 index.html 文件。 在這個基礎工程中, 我們使用bootstrap的樣式,直接引入一個cdn即可. 然后添加一個html標簽 <div id="app"></div>,我們的app就會注入到這個div中。 最后再引入 <script src="bundle.js"></script>,這是最后打包生成的js代碼。
以下是完整的代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="external nofollow" > </head> <body> <div id="app"></div> <script src="bundle.js"></script> </body> </html>
建立一個新的文件夾 src. 我們app的大部分代碼都將放在這個文件夾里面。在 src中建立 app.js,作為React App的根組件, 其他所有的組件都會注入到這個跟組件中。
首先我們需要導入react,現在都已經用ES6的語法, import React from 'react'; , 然后我們要引入react-dom. 這里面有react中最重要的一個虛擬dom的概念.引入代碼:import ReactDOM
from 'react-dom';
現在需要引入的依賴都已經完畢我們可以寫第一個組件了:
class App extends React.Component { render(){ // Every react component has a render method. return( // Every render method returns jsx. Jsx looks like HTML, but it's actually javascript and functions a lot like xml, with self closing tags requiring the `/` within the tag in order to work propperly <div> Hello World </div> ); } }
注意這里"Hello World"寫在 div中. 所有的jsx代碼都需要寫在一個父div中.
最后我們需要把我們寫好的組件render給Dom,這里就需要用到 ReactDOM.render 方法.
在 App.js 的下面添加: ReactDOM.render(<App />, document.getElementById('app'));
第一個參數就是我們App的根組件, 寫作<App />的形式. 第二個參數就是我們的APP將要主要的DOM元素. 在這個項目中,就是我們在index中寫的id為app的 div標簽。
Ok,我們的APP結構已經出來了,經典的hello world已經實現。馬上我們就在這個基礎上再實現經典的todo app。大致的原型就有一個輸入框用來輸入代辦事項然后添加到事件列表中。事件列表中每一個代辦事項被點擊就會標注一條刪除線表示完成,點擊后面的刪除按鈕則會將其從列表中刪除。通過完成這個APP的過程你將學會一個完整的react app的所有的基本構建塊。
生命周期方法和兩種形式的組件構建
我們從一些小的模塊開始起步.一個組件component就是一個react app的構件塊. 有兩種形式的組件: 類組件(Class)和函數型組件(Functional). 在這個項目中,這兩種形式的組件我們都會使用, 并且使用生命周期的鉤子,同時也會使用state和props兩個react中重要的屬性。
首先在 src文件夾中新建components 文件夾,你的文件結構就是這樣 ~/src/components。
然后在components中新建文件 ToDoApp.js。 對于所有的react組件我們都需要在頭部引入reactimport React from 'react';。
下面我們寫一個類組件. 所有的class 組件有一個render方法用來返回jsx。
ToDoApp的class就如下所示:
class ToDoApp extends React.Component { render() { return ( <div>To Do App</div> ); } }
為了將這個組件注入到我們的APP中, 首先我們需要輸出它。 在這個組件代碼底部添加 export default ToDoApp;。
然后在app.js頂部我們添加 import ToDoApp from '.components/ToDoApp'; 導入組件用來代替 Hello World 。 render中替換為新的jsx代碼 <ToDoApp />半閉合類型的標簽即可。
然后在瀏覽器中你就可以看到"To Do App" 代替了原來的 "Hello World"!這樣我們就完成了將第一個子組件嵌入到根組件之中了,這就是構建react app的常規模式。下面繼續完善我們的組件。
返回到ToDoApp 中來構建我們的第一個代辦事項列表。首先我們使用bootstrap來構建比較方便且美觀。 用下面的jsx替換當前render方法中 return 中的jsx:
<div className="row"> <div className="col-md-10 col-md-offset-1"> <div className="panel panel-default"> <div className="panel-body"> <h2>My To Do App</h2> <hr/> List goes here. </div> </div> </div> </div>
現在打開瀏覽器, 你將會看到一個標題 "My To Do App" 下面跟隨一個bootstrap的panel組件里面寫有 "List Goes Here",我們將在這個地方構建列表。 那么我們如何將數據存儲在我們的列表中呢? 答案就是使用 state. 每一個類組件都有 state 屬性,可以通過 this.state在組件任何位置獲取并且用 this.setState({
key: "value" })這種方法來更新狀態。但是除非必要我們比較少使用state,這里暫時先使用作為了解,后期會使用redux來管理狀態。
在ToDoApp中我們可以使用許多生命周期方法的鉤子, 其中一個就是componentWillMount。 這個方法的執行是在頁面加載并且render方法之前。可以在其中獲取列表數據,在我們的APP中直接用一個虛擬的數組提供。(值得注意的是componentWillMount會引起很多小問題,因此真實項目中盡量不要使用,而是應該用componentDidMount)。
在 ToDoApp中 render 方法之前添加:
componentWillMount(){ // run before the render method this.setState({ // add an array of strings to state. list: ['thing1', 'thing2', 'thing3'] }) };
現在我們獲取了一個虛擬列表,需要重點注意的就是react依賴于state和props,只有當state和props改變的時候react組件才會刷新。
現在我們添加列表到這個view里,這里不是直接簡單的在里面修改jsx,而是再創建一個新的組件來構建列表,這次我們學習使用函數型組件,需要注意的是函數型組件沒有生命周期方法和state屬性,它僅僅是一個返回jsx的函數,并且參數是props。
那么props到底是什么呢?props是從父組件傳遞進子組件的數據的名字,這是一個很重要的概念,也是react app數據傳遞的最典型與最推薦的方法。通常我們將數據保持在app的頂端組件,通過組件讓數據流下來保證APP的精確運行。這些數據和props的一些處理可能會影響APP的運行,但是假如你按照這個課程的實踐流程來做,這些影響都會很小。
再新建一個components文件夾并在其中新建一個List.js作為我們要創建的函數型組件。用const來新建一個函數,參數名字寫作props。
函數形式如下所示:
const List = (props) => { // we're using an arrow function and const variable type, a ES6 features return ( <div> I'm a list!!! </div> ) }; export default List;
在 ToDoApp.js引入 List用List 組件替換 List goes here.,寫法為 <List />.現在在瀏覽器中就可以看到"I'm a list!!!"
現在我們來把這個變成真實的列表,首先就需要通過props傳遞數據,我們把這個從state中獲取的數據list通過命名為listItems的props傳遞,寫作: <List listItems={this.state.list} /> ,現在 List 已經通過props獲取了 ToDoApp中的數據。
然后在 List 組件中我們需要render一個列表,先用下面的jsx代碼代替:
<div> <ul> { list // this is a variable we'll define next } </ul> </div>
注意這個大括號,js可以在這里面執行并將返回添加到view里。首先我們定義一個列表變量:
const list = props.listItems.map((el, i)=>( // All where doing here is getting the items listItems prop // (which is stored in the state of the parent component) // which is an array, and we're running the .map method // which returns a new array of list items. The key attribute is // required, and must be unique. <li key={i}><h3>el</h3></li> ));
完整的組件如下:
import React from 'react'; const List = (props) => { const list = props.listItems.map((el, i)=>( <li key={i}><h3>el</h3></li> )); return ( <div> <ul> { list } </ul> </div> ) }; export default List;
現在打開瀏覽器就可以看到一列列表了。接下來就是給我們的項目加入功能了,包括添加新的事項,標注事項完成和刪除列表中事項。
給APP添加功能
1.函數型組件
首先我們需要添加一個input元素以便可以輸入代辦事項。因此我們在components文件夾中新建一個Input.js,然后在其中創建并輸出一個名叫Input的函數型組件。
把下面的jsx代碼粘貼到你的函數型組件return之中:
<form> <div className="form-group"> <label htmlFor="listInput"> Email address </label> <input type="text" className="form-control" id="listItemInput" placeholder="Add new todo" /> <button className="btn btn-primary"> Add Item </button> </div> </form>
2. Input
現在我們的jsx沒有做任何特殊的事情,僅僅是一個基本的html視圖,不過我們先測試一下把其導入到ToDoApp.js,形式就是<Input/>。
這時候會發現一個輸入框和按鈕的視圖,這個組件的靜態視圖已經寫好了,下面就需要添加功能了。
3. Props
首先我們需要做的是如何獲取輸入框的值,因為這個輸入框的值需要在其他組件中獲取,所以我們并不想要在Input組件中來處理這個數據存儲。事實上,在子組件中存儲數據在任何時候都是不推薦的,我們應該將數據存儲在app的頂端組件并且通過props傳遞下來。
另一個需要記住的是即使我們目前把數據存儲在了上層的 ToDoApp 組件,后期還是會用redux來代替來處理整個app的數據。這里先僅僅使用react的state來實現。
ok,我們在ToDoApp的 componentWillMount的setState中新增一個newToDo屬性用來存儲輸入框的值。
componentWillMount(){ this.setState({ list: ['thing1', 'thing2', 'thing3'], newToDo: 'test' }) };
同樣的就可以通過在<Input />上通過props傳遞下去。
4. 解構(Destructuring)
在Input.js中我們通過參數props可以獲得上級組件傳遞下來的值, 但是還可以用ES6的新特性解構來作為參數,這樣看起來更加酷!
把Input組件的props參數修改為({
value })這樣的參數形式,這樣可以把props這個對象參數解構為一個個鍵值對。直接看個小例子來就很明白了:
var props = { name: 'hector', age: 21 } function log(props){ console.log(props.name); console.log(props.age); } log(props);
is the same as this:
let props = { name: 'hector', age: 21 } log = ({name, age}) => { console.log(name); console.log(age); } log(props);
5. setState
上面的newToDo僅僅是添加了一個state用來存儲輸入框的值,給定一個值,輸入框就會顯示,明顯還不是我們要的效果,我們需要做的是基于輸入框的值的改變來動態改變這個state。
為了實現這個功能,我們需要再添加一個onChange方法同樣利用props傳進Input組件: onChange={onChange}, 然后解構參數就是({ onChange, value })。
然后在 ToDoApp.js的componentWillMount 添加一個新的方法 onInputChange。這個方法有一個參數event, 它將要捕獲用戶在輸入框輸入的值。
onInputChange = (event) => { this.setState({ newToDo: event.target.value}); // updates state to new value when user changes the input value };
6. 添加新列表事項
現在需要向列表中添加新的事項,也就是在提交后能把輸入框的值存儲并顯示到列表中。我們需要再新建一個onInputSubmit的方法,參數同樣是event,函數體內首先需要寫 event.preventDefault(),然后用 setState 方法把新事項添加到列表數組中,但是,一定要注意我們的state應該是immutable的,這是react中必須遵循的一個準則,這樣才能保證對比性與可靠性。
為了實現這個功能, 需要用到this.setState 回調函數,參數為previousState:
this.setState((previousState)=>({ list: previousState.list.push(previousState.newToDo) }))
正如我上面的描述,最開始寫state的時候很多人都會犯這樣的錯誤,直接用push這樣的方法,修改了state,這樣就不算immutable的,我們一定要保證絕不直接修改原state。
這里又可以用到ES6中的新特性了,擴展操作符,它通過遍歷舊數組返回一個新數組,使舊的數組保持原樣,這樣我們就把事項添加到列表數組末尾:
this.setState((previousState)=>({ list: [...previousState.list, previousState.newToDo ], // the spread opperator is called by using the ... preceding the array }));
在提交添加新事項的同時,需要將newToDo重置為'':
this.setState((previousState)=>({ list: [...previousState.list, previousState.newToDo ], newToDo: '' }));
7. 劃掉事項
是時候添加劃掉事項的功能了。為了實現這個功能需要添加一個新的屬性用來標注是否需要劃掉,因此需要改變原來的數組為一個對象數組,每一個事項都是一個對象,一個key為item表示原來的事項內容,一個key為done用布爾值來表示是否劃掉。 然后先把原來的onInputSubmit方法修改,同樣要注意immutable,使用擴展操作符如下:
onInputSubmit = (event) => { event.preventDefault(); this.setState((previousState)=>({ list: [...previousState.list, {item: previousState.newToDo, done: false }], // notice the change here newToDo: '' })); };
屬性done添加完成后就需要新增一個方法當點擊事項時候來改變這個值:
onListItemClick = (i) => { // takes the index of the element to be updated this.setState((previousState)=>({ list: [ ...previousState.list.slice(0, i), // slice returns a new array without modifying the existing array. Takes everything up to, but not including, the index passed in. Object.assign({}, previousState.list[i], {done: !previousState.list[i].done}), // Object.assign is a new ES6 feature that creates a new object based on the first param (in this case an empty object). Other objects can be passed in and will be added to the first object without being modified. ...previousState.list.slice(i+1) // takes everything after the index passed in and adds it to the array. ] })) };
然后把這個方法通過props傳遞給List 組件,這里就沒有使用解構參數傳遞,用來和Input的做對比。因為這個函數需要一個參數就是當前列表的序列號,但是肯定不能直接call這個函數否則會報錯,因此使用bind方法,出入i參數:
onClick={props.onClick.bind(null, i)}
當然還有另一種方法:
onClick={() => props.onClick(i)}
然后在事項內容的span標簽上添加 onClick 方法,改變當前事項的done值后,在通過判斷此布爾值來進行樣式的修改添加或者劃掉刪除線。
<span style={ el.done ? {textDecoration: 'line-through', fontSize: '20px'} : {textDecoration: 'none', fontSize: '20px'} } onClick={props.onClick.bind(null, i)} >
8. 刪除事項
最后我們在添加刪除事項的功能,這個和劃掉事項非常相似,我們只需要新增一個刪除按鈕,然后再新增一個方法修改列表,具體代碼如下:
<button className="btn btn-danger pull-right" > x </button>
deleteListItem = (i) => { this.setState((previousState)=>({ // using previous state again list: [ ...previousState.list.slice(0, i), // again with the slice method ...previousState.list.slice(i+1) // the only diffence here is we're leaving out the clicked element ] })) };
把deleteListItem 方法傳遞到列表組件中然后在刪除按鈕上綁定即可,仿照上一個自己寫一下就好。
現在我們有一個完整功能的APP了,是不是感覺很cool,這個就是不用redux時候的形態了,但是你會發現當狀態越來越復雜時候很繁瑣,因此我們下面就要介紹redux來管理狀態了。
遷移到redux的準備工作
截至目前我們已經學會如何用webpack和babel搭建react應用,構建類組件和函數型組件并處理state,添加功能。然而這只是基本滿足一個小型應用的需求,隨著app的增長,處理數據和行為會越來越吃力,這就是要引入redux的必要性。
那么redux如何處理數據?首先,redux給你的app一個單一的state對象,與flux等根據view來劃分為多個state對象正好相反。你可能會有疑問,一個單一的對象來處理一個復雜的app豈不是非常復雜?redux采用的方法是把數據處理分為reducer functions、action creators和actions然后組合在一起工作流線型的處理數據。
1. 首先安裝必須的依賴
首先安裝 redux and react-redux
npm install --save redux npm install --save react-redux
然后安裝 redux middleware,這里就先安裝 redux-logger,它的功能是幫助我們開發。
npm install --save redux-logger
還有一些常用的中間件,比如 redux-thunk and redux-promise, 但是在我們的這個項目中暫時先不需要,可以自行去github了解。
2. 構建
使用redux構建react應用一般都有一個標準的模板,可能不同模板形式上有區別,但是思想都是一樣的,下面就先按照一種文件結構來構建。
首先我們在src中新建一個文件夾redux,然后在其中新建一個文件configureStore.js,添加以下代碼:
import { createStore, applyMiddleware, combineReducers } from 'redux'; import createLogger from 'redux-logger';
createStore 是由redux提供的用來初始化store的函數, applyMiddleware是用來添加我們需要的中間件的。
combineReducers 用來把多個reducers合并為一個單一實體。
createLogger 就是我們這里唯一使用的一個中間件,可以console出每一個action后數據的詳細處理過程,給調試帶來了很大方便。
然后添加下面代碼:
const loggerMiddleware = createLogger(); // initialize logger const createStoreWithMiddleware = applyMiddleware( loggerMiddleware)(createStore); // apply logger to redux
這里暫時沒有完成,需要后面的模塊寫完了再導入到這里繼續來完成。
3. 模塊Modules
在 src/redux/ 新建一個文件夾 modules。在這個文件夾中我們將存放所有的reducers,action creators和constants。這里我們使用的redux組織結構叫做ducks,思想就是把相關的reducers,action creators和constants都放在一個單獨的文件中,而不是分開放在多個文件中,這樣修改一個功能時候直接在一個文件中修改就可以。
在 modules 文件中新建 'toDoApp.js',注意這里的命名是依據容器組件的名字來命名,這個也是規范,容易管理代碼。
現在我們可以開始創建initial state和 reducer function,這其實非常簡單,state就是一個js對象,reducer就是js的switch語句:
const initialState = {}; //The initial state of this reducer (will be combined with the states of other reducers as your app grows) export default function reducer(state = initialState, action){ // a function that has two parameters, state (which is initialized as our initialState obj), and action, which we'll cover soon. switch (action.type){ default: return state; } }
4. 完善Store
現在我們已經完成了第一個reducer,可以將其添加到 configureStore.js 中去了, 導入: import toDoApp from './modules/toDoApp';
然后用combineReducers 來組合當前的reducer,因為未來會有更多的模塊加入。
const reducer = combineReducers({ toDoApp });
最后在底部加入下面完整的代碼:
const configureStore = (initialState) => createStoreWithMiddleware(reducer, initialState); export default configureStore; Cool. We're done here.
5. Connect
現在我們已經有了一個reducer,那么怎么和app建立聯系呢?這需要兩步工作。
前面已經講過類組件和函數型組件,有時候也可以稱為smart components和dumb components,這里我們新增一種容器組件,顧名思義,這種組件就是作為一個容器用來給組件提供actions和state。
下面來創建第一個容器組件,首先在 /src/ 下新增一個文件夾containers,然后再其下面新建一個文toDoAppContainer.js。
在文件頂部首先導入 connect 用來將容器和組件聯系在一起,
import { connect } from 'react-redux'; import ToDoApp from '../components/ToDoApp.js'
connect 這個函數被調用兩次, 第一次是兩個回調函數: mapStateToProps and mapDispatchToProps。 第二次是把state和dispatch傳入組件的時候。這里的dispatch又是什么呢?
當我們需要在redux中發生某些行為時候,就需要調用dispatch函數傳遞一個action然后調用reducer這一套流程。因為我們還沒有編寫具體的行為,這里就暫時空白,后面再補,代碼形式如下:
function mapStateToProps(state) { return { toDoApp: state.toDoApp // gives our component access to state through props.toDoApp } } function mapDispatchToProps(dispatch) { return {}; // here we'll soon be mapping actions to props }
然后在底部添加:
export default connect( mapStateToProps, mapDispatchToProps )(ToDoApp);
1、Provider
redux的基本工作已經完成,最后一步就是返回到app.js 文件, 首先我們不再需要導入 ToDoApp 組件,而是用容器組件ToDoAppContainer來替代,然后需要導入 configureStore 函數和 Provider,在頭部添加代碼:
import { Provider } from 'react-redux'; import ToDoAppContainer from './containers/ToDoAppContainer'; import configureStore from './redux/configureStore';
configureStore is the function we created that takes our combined reducers and our redux middleware and mashes them all together. Let's intialize that with the following line:
const store = configureStore();
然后return的jsx中同樣需要把ToDoApp 改為 ToDoAppContainer,然后需要用Provider 組件將其包裹,它的作用就是將整個app的state傳遞給它所包裹的容器,從而使容器組件可以獲取這些state。
<Provider store={store}> // we pass the store through to Provider with props <ToDoAppContainer /> </Provider>
現在整個redux的基本結構已經搭建起來,下一步就可以把整個行為邏輯代碼補充進去就可以了。
Redux Actions 和 Reducers
搭建起redux的基本結構后,就可以填充redux的元素了,簡單來說我們只需要記住四個概念, Types, Actions, Action Creators, and Reducers。然后把這些元素用ducks的文件組織結構組織起來就可以了。
Ducks
規則
在module中我們需要遵循下面的代碼風格和命名方式:
須用 export default 輸出名為 reducer()的函數
須用 export 輸出 函數形式的action creators
須用 npm-module-or-app/reducer/ACTION_TYPE的命名形式來命名action types,因為到后期很多reducer,不同的人協同工作難免會出現命名重復,這樣子加上app和模塊的前綴的話就不會出現命名沖突的問題。
須用大寫的蛇形方式UPPER_SNAKE_CASE來命名action types。
Types
這個types就是上面第三條中需要按照ducks的規范命名的常量名字,將其寫在文件的頂部,當action 觸發時候會傳遞給reducer,reducer的switch語句會根據這個type來進行相應的數據處理。
const ADD_ITEM = 'my-app/toDoApp/ADD_ITEM'; const DELETE_ITEM = 'my-app/toDoApp/DELETE_ITEM';
Actions
Actions 就是一個至少包含type的簡單的js對象,同時可以包含數據以便傳遞給reducer。當用戶在頁面上觸發了某種行為,一個aciton creator將會發送aciton給reducer做數據處理。
action示例如下:
{ type: ADD_ITEM, item: 'Adding this item' } { type: DELETE_ITEM, index: 1 } { type: POP_ITEM }
Action Creators
Action creators 是創建acitons并傳遞給reducer的函數,它通常返回一個action對象,有時候借用thunk這樣的中間件也可以返回dispatch多個actions,在我們的app中為了簡化暫時不涉及這個模式。
function addItem(item){ return { type: ADD_ITEM, item // this is new ES6 shorthand for when the key is the same as a variable or perameter within the scope of the object. It's the same as item: item } }
Reducers
reducer是唯一可以觸碰store的元素,初始值為initialState,形式上就是一個簡單的switch語句,但是注意不能直接改變state,因為state是immutable。也就是說我們不能直接使用.pop or .push這些方法操作數組。
下面是示例代碼:
const initialState = { list: [] }; export default function reducer(state = initialState, action){ switch (action.type){ case ADD_ITEM: return Object.assign( {}, state, { list: [...state.list, action.item]} // here we see object.assign again, and we're returning a new state built from the old state without directly manipulating it ) default: return state; } }
概念已經介紹完畢,下面開始將原來的功能邏輯用redux重寫。
1. Initial state
首先我們在 src/redux/modules/toDoApp中聲明initialState。
const initialState = { list: [{item: 'test', done: false}] // just added this to test that state is being passed down propperly, newToDo: '' }; export default function reducer(state = initialState, action){ switch (action.type){ default: return state; } }
現在在 ToDoApp.js的 render() 方法中return之前添加console.log(this.props) 會打印出下面的對象:
toDoApp: Object list: Array[1] 0: "test" length: 1 __proto__: Array[0] __proto__: Object __proto__: Object
測試通過,我們就可以傳遞這些數據給子組件了,這里就可以把原來List組件的 listItems prop和Input的value prop替換掉了。
<List onClick={this.onListItemClick} listItems={this.props.toDoApp.list} deleteListItem={this.deleteListItem} /> <Input value={this.props.toDoApp.newToDo} onChange={this.onInputChange} onSubmit={this.onInputSubmit} />
這里只是替換掉了數據,下面還需要把action也替換。
3. Input action
這個過程就是把我們原來在ToDoApp 組件的行為邏輯全部遷移到redux文件夾下的 toDoApp module中去。
const INPUT_CHANGED = 'INPUT_CHANGED'; export function inputChange(newToDo){ return { type: INPUT_CHANGED, newToDo } }
然后在reducer的switch中新增如下處理:
case INPUT_CHANGED: return Object.assign( {}, state, {newToDo: action.value} );
在 toDoAppContainer.js 的 mapDispatchToProps 函數就需要返回相應的action,首先導入 inputChange, 具體代碼如下:
import { connect } from 'react-redux'; import ToDoApp from '../components/ToDoApp.js' import { inputChange } from '../redux/modules/toDoApp'; // we added this function mapStateToProps(state) { return { toDoApp: state.toDoApp // gives our component access to state through props.toDoApp } } function mapDispatchToProps(dispatch) { return { inputChange: (value) => dispatch(inputChange(value)) // we added this }; } export default connect( mapStateToProps, mapDispatchToProps )(ToDoApp);
這樣state和action都傳遞給了toDoApp然后再通過props傳遞給子組件就可以使用了,具體都可以看項目最終代碼。
4. 其他 actions
其他acitons的代碼模式跟上面的基本一樣,這里不在贅述。
以上是“如何搭建一個react項目”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。