摘要:先來一波硬廣我的博客歡迎觀光傳送門這個小應(yīng)用使用創(chuàng)建演示地址,地址。這是之前的的版,之前的演示,改寫自的課程。
React-Redux-Appointment
先來一波硬廣:我的博客歡迎觀光:傳送門
這個小應(yīng)用使用Create React App創(chuàng)建,演示地址:https://liliang-cn.github.io/react_redux_appointment,repo地址:https://github.com/liliang-cn/react_redux_appointment。
這是之前的React_appointment的Redux版,之前的演示,改寫自Lynda的課程Building a Web Interface with React.js。
文件結(jié)構(gòu)最終的文件目錄如下:
react_redux_appointment/ README.md node_modules/ package.json public/ index.html favicon.ico src/ actions/ index.js components/ AddForm.js AptList.js Search.js Sort.js constants/ index.js containers/ AddForm.js App.js reducers/ apts.js formExpanded.js index.js openDialog.js orderBy.js orderDir.js query.js index.css index.js用到的模塊
{ "name": "react_redux_appointment", "version": "0.1.0", "private": true, "homepage": "https://liliang-cn.github.io/react_redux_appointment", "devDependencies": { "react-scripts": "0.8.4" }, "dependencies": { "axios": "^0.15.3", "gh-pages": "^0.12.0", "lodash": "^4.17.2", "material-ui": "^0.16.5", "moment": "^2.17.1", "react": "^15.4.1", "react-dom": "^15.4.1", "react-redux": "^5.0.1", "react-tap-event-plugin": "^2.0.1", "redux": "^3.6.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "deploy": "yarn build && gh-pages -d build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } }所有的state
小應(yīng)用一共有六個狀態(tài),其中的formExpanded和openDialog是界面組件的狀態(tài),
剩下的四個分別是apts(代表所有的預約)、orderBy(根據(jù)什么來排列預約列表,根據(jù)姓名還是根據(jù)日期)、
orderDir(排列列表的方向,是增序還是降序)、query(搜索的關(guān)鍵字)。
在應(yīng)用中可能產(chǎn)生的actions有七種:
addApt,即新建預約
deleteApt, 即刪除預約
toggleDialog, 即顯示、隱藏警告框
toggleFormExpanded, 顯示/隱藏表單
query,即查詢
changeOrderBy,即改變排序的關(guān)鍵字
changeOrderDir, 即改變排序方向
定義七個常量來代表這些action的類型:
constants/index.js:
export const ADD_APT = "ADD_APT"; export const DELETE_APT = "DELETE_APT"; export const TOGGLE_DIALOG = "TOGGLE_DIALOG"; export const TOGGLE_FORM_EXPANDED = "TOGGLE_FORM_EXPANDED"; export const QUERY = "QUERY"; export const CHANGE_ORDER_BY = "CHANGE_ORDER_BY"; export const CHANGE_ORDER_DIR = "CHANGE_ORDER_DIR";
actions/index.js:
import { ADD_APT, DELETE_APT, TOGGLE_DIALOG, TOGGLE_FORM_EXPANDED, QUERY, CHANGE_ORDER_BY, CHANGE_ORDER_DIR } from "../constants"; export const addApt = (apt) => ({ type: ADD_APT, apt }); export const deleteApt = (id) => ({ type: DELETE_APT, id }); export const toggleDialog = () => ({ type: TOGGLE_DIALOG }); export const toggleFormExpanded = () => ({ type: TOGGLE_FORM_EXPANDED }); export const query = (query) => ({ type: QUERY, query }); export const changeOrderBy = (orderBy) => ({ type: CHANGE_ORDER_BY, orderBy }); export const changeOrderDir = (orderDir) => ({ type: CHANGE_ORDER_DIR, orderDir });UI組件 樣式
使用Material-UI需要引入Roboto字體:
src/index.css
@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500"); body { margin: 0; padding: 0; font-family: Roboto, sans-serif; }表單組件
components/addForm.js:
import React from "react"; import {Card, CardHeader, CardText} from "material-ui/Card"; import TextField from "material-ui/TextField"; import DatePicker from "material-ui/DatePicker"; import TimePicker from "material-ui/TimePicker"; import RaisedButton from "material-ui/RaisedButton"; import Paper from "material-ui/Paper"; import Divider from "material-ui/Divider"; import Dialog from "material-ui/Dialog"; import FlatButton from "material-ui/FlatButton"; import moment from "moment"; const paperStyle = { width: 340, margin: "0 auto 20px", textAlign: "center" }; const buttonStyle = { margin: 12 }; // open, toggleDialog是兩個布爾值,handleAdd,formExpanded, toggleFormExpanded是三個回調(diào)函數(shù),來自于../containers/AddForm.js中的容器從store中獲取并傳遞下來的 const AddForm = ({handleAdd, open, toggleDialog, formExpanded, toggleFormExpanded}) => { let guestName, date, time, note; // 點擊Add時會先首先檢查是否所有的值都有輸入,如果輸入合法則發(fā)起ADD_APT的action然后發(fā)起切換表單顯示的action,如果輸入有誤則發(fā)起TOGGLE_DIALOG的action const onAdd = () => { guestName && date && time && note ? handleAdd({guestName, date, time, note}) && toggleFormExpanded() : toggleDialog() }; // 這兩個函數(shù)用來獲取輸入的日期和時間 const handleDateChange = (event, aptDate) => { date = moment(aptDate).format("YYYY-MM-DD") }; const handleTimeChange = (event, aptTime) => { time = moment(aptTime).format("hh:mm") }; const actions = [搜索表單]; return ( // Card組件的expanded的值是一個布爾值,來自于父組件傳下來的formExpanded,即應(yīng)用的狀態(tài)formExpanded,用來確定是否顯示表單 ); }; export default AddForm;// Dialog組件的open的值也是一個布爾值,來自于父組件傳下來的open,即應(yīng)用的狀態(tài)openDialog,用來驗證表單 guestName = e.target.value.trim()} /> note = e.target.value.trim()} />
components/Search.js:
import React from "react"; import TextField from "material-ui/TextField"; const Search = ({handleSearch}) => { return (排列選擇); }; export default Search;handleSearch(e.target.value) } />
components/Sort.js:
import React from "react"; import SelectField from "material-ui/SelectField"; import MenuItem from "material-ui/MenuItem" const Sort = ({ orderBy, orderDir, handleOrderByChange, handleOrderDirChange }) => { return (預約列表); }; export default Sort;{handleOrderByChange(value)}} > {handleOrderDirChange(value)}} >
這個組件的作用就是顯示預約列表,接受父組件傳來的apts數(shù)組和handleDelete函數(shù),在點擊RaisedButton的時候?qū)pt.id傳入handleDelete并執(zhí)行。
components/AptList.js:
import React from "react"; import {List, ListItem} from "material-ui/List"; import {Card, CardActions, CardHeader, CardTitle, CardText} from "material-ui/Card"; import RaisedButton from "material-ui/RaisedButton"; const buttonStyle = { width: "60%", margin: "12px 20%", }; const AptList = ({apts, handleDelete}) => { return (處理不同的actions 處理表單的顯示和隱藏); }; export default AptList;Appointments List
// 這里的i也可以直接用apt.id {apts.map((apt, i) => (
))} {apt.note} handleDelete(apt.id)} />
reducers/formExpanded.js:
import { TOGGLE_FORM_EXPANDED } from "../constants"; // formExpanded默認為false,即不顯示,當發(fā)起類型為TOGGLE_FORM_EXPANDED的action的時候,將狀態(tài)切換為true或者false const formExpanded = (state=false, action) => { switch (action.type) { case TOGGLE_FORM_EXPANDED: return !state; default: return state; } }; export default formExpanded;表單驗證錯誤的提示對話框
reducers/openDialog.js:
import { TOGGLE_DIALOG } from "../constants"; // 這個action是由其他action引發(fā)的 const openDialog = (state=false, action) => { switch (action.type) { case TOGGLE_DIALOG: return !state; default: return state; } }; export default openDialog;處理新建預約和刪除預約
reducers/apts.js:
import { ADD_APT, DELETE_APT } from "../constants"; // 用唯一的id來標識不同的預約,也可以直接用時間戳new Date() let id = 0; // 根據(jù)傳入的數(shù)組和id來執(zhí)行刪除操作 const apts = (state=[], action) => { const handleDelete = (arr, id) => { for(let i=0; i查詢和排列方式 這三個函數(shù)的作用就是根據(jù)action傳入的數(shù)據(jù),更新state里的對應(yīng)值,在這里并不會真正的去處理預約的列表。
reducers/orderBy.js:
import { CHANGE_ORDER_BY } from "../constants"; const orderBy = (state=null, action) => { switch (action.type) { case CHANGE_ORDER_BY: return action.orderBy default: return state; } }; export default orderBy;reducers/orderDir.js:
import { CHANGE_ORDER_DIR } from "../constants"; const orderDir = (state=null, action) => { switch (action.type) { case CHANGE_ORDER_DIR: return action.orderDir default: return state; } }; export default orderDir;reducers/query.js:
import { QUERY } from "../constants"; const query = (state=null, action) => { switch (action.type) { case QUERY: return action.query; default: return state; } } export default query;合成reducersreducers/index.js:
import { combineReducers } from "redux"; import apts from "./apts"; import openDialog from "./openDialog"; import formExpanded from "./formExpanded"; import query from "./query"; import orderBy from "./orderBy"; import orderDir from "./orderDir"; // redux提供的combineReducers函數(shù)用來將處理不同部分的state的函數(shù)合成一個 // 每當action進來的時候會經(jīng)過每一個reducer函數(shù),但是由于action類型(type)的不同 // 只有符合(switch語句的判斷)的reducer才會處理,其他的只是將state原封不動返回 const reducers = combineReducers({ apts, openDialog, formExpanded, query, orderBy, orderDir }); export default reducers;容器組件containers/AddForm.js:
import { connect } from "react-redux"; import { addApt, toggleDialog, toggleFormExpanded } from "../actions"; import AddForm from "../components/AddForm"; // AddForm組件可通過props來獲取兩個state:open和formExpanded const mapStateToProps = (state) => ({ open: state.openDialog, formExpanded: state.formExpanded }); // 使得AddForm組件可以通過props得到三個回調(diào)函數(shù),調(diào)用即可相當于發(fā)起action const mapDispatchToProps = ({ toggleFormExpanded, toggleDialog, handleAdd: newApt => addApt(newApt) }); // 使用react-redux提供的connect函數(shù),可以將一個組件提升為容器組件,容器組件可直接獲取到state、可以直接使用dispatch。 // 這個connect函數(shù)接受兩個函數(shù)作為參數(shù),這兩個作為參數(shù)的函數(shù)的返回值都是對象, 按約定他們分別命名為mapStateToProps,mapDispatchToProps // mapStateToProps確定了在這個組件中可以獲得哪些state,這里的話只用到了兩個UI相關(guān)的state:open和formExpanded,這些state都可通過組件的props來獲取 // mapDispatchToProps本來應(yīng)該是返回對象的函數(shù),這里比較簡單,直接寫成一個對象,確定了哪些action是這個組件可以發(fā)起的,也是通過組件的props來獲取 // connect函數(shù)的返回值是一個函數(shù),接受一個組件作為參數(shù)。 export default connect(mapStateToProps, mapDispatchToProps)(AddForm);containers/App.js:
import React from "react"; import { connect } from "react-redux"; import MuiThemeProvider from "material-ui/styles/MuiThemeProvider" import injectTapEventPlugin from "react-tap-event-plugin"; injectTapEventPlugin(); import AppBar from "material-ui/AppBar"; import Paper from "material-ui/Paper"; import AddForm from "../containers/AddForm"; import Search from "../components/Search"; import Sort from "../components/Sort"; import AptList from "../components/AptList"; import { deleteApt, query, changeOrderBy, changeOrderDir } from "../actions"; const paperStyle = { minHeight: 600, width: 360, margin: "20px auto", textAlign: "center" }; const App = ({ apts, dispatch, orderBy, orderDir, handleSearch, handleDelete, handleOrderByChange, handleOrderDirChange }) => (入口文件); // 處理搜索和排序,返回處理后數(shù)組 const handledApts = (apts, query, orderBy, orderDir) => { const filterArr = (arr, query) => { return arr.filter(item => ( item.guestName.toLowerCase().indexOf(query) !== -1 || item.date.indexOf(query) !== -1 || item.time.indexOf(query) !== -1 || item.note.toLowerCase().indexOf(query) !== -1) ); }; const sortArr = (arr, orderBy, orderDir) => { if (orderBy && orderDir) { return arr.sort((apt1, apt2) => { const value1 = apt1[orderBy].toString().toLowerCase(); const value2 = apt2[orderBy].toString().toLowerCase(); if (value1 < value2) { return orderDir === "asc" ? -1 : 1; } else if (value1 > value2) { return orderDir === "asc" ? 1 : -1; } else { return 0; } }) } else { return arr; } }; if (!query) { return sortArr(apts, orderBy, orderDir); } else { return sortArr(filterArr(apts, query), orderBy, orderDir); } }; // App組件可通過props來獲取到四個state:query, orderBy, orderDir, apts // 這里是真正處理搜索和排序的地方,并不是直接將state中的apts返回,而是調(diào)用handleApts,返回處理的數(shù)組 const mapStateToProps = (state) => ({ query: state.query, orderBy: state.orderBy, orderDir: state.orderDir, apts: handledApts(state.apts, state.query, state.orderBy, state.orderDir), }); // App組件可通過props來獲取到四個函數(shù),也就是發(fā)起四個action:handleSearch,handleDelete,handleOrderByChange,handleOrderDirChange const mapDispatchToProps = ({ handleSearch: searchText => query(searchText), handleDelete: id => deleteApt(id), handleOrderByChange: orderBy => changeOrderBy(orderBy), handleOrderDirChange: orderDir => changeOrderDir(orderDir) }); export default connect(mapStateToProps, mapDispatchToProps)(App); src/index.js:
import React from "react"; import ReactDOM from "react-dom"; import { createStore } from "redux"; import { Provider } from "react-redux"; import App from "./containers/App"; import "./index.css"; import reducers from "./reducers"; // 使用createStore表示應(yīng)用的store,傳入的第一個參數(shù)是reducers,第二個參數(shù)是Redux的調(diào)試工具 const store = createStore(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()); // 使用react-redux提供的Provider組件,使App組件及子組件可以得到store的相關(guān)的東西,如store.getState(),store.dispatch()等。 ReactDOM.render(結(jié)尾, document.getElementById("root") ); React提供的是通過state來控制控制UI和單向數(shù)據(jù)流動,
Redux提供的是單一數(shù)據(jù)源和只能通過action和reducer來處理state的更新。以其中的點擊按鈕顯示新建預約表單的過程來捋一捋React、React-Redux的邏輯(靈感來源于自Cory House大神):
用戶:點擊按鈕
React:哈嘍,action生成函數(shù)toggleFormExpanded,有人點擊了展開新建預約的表單。
Action:收到,謝謝React,我馬上發(fā)布一個action也就是{type:TOGGLE_FORM_EXPANDED}告訴reducers來更新state。
Reducer:謝謝Action,我收到你的傳過來要執(zhí)行的action了,我會根據(jù)你傳遞進來的{type:TOGGLE_FORM_EXPANDED},先復制一份當前的state,然后把state中的formExpanded的值更新為true,然后把新的state給Store。
Store:嗯,Reducer你干得漂亮,我收到了新的state,我會通知所有與我連接的組件,確保他們會收到新state。
React-Redux:啊,感謝Store傳來的新數(shù)據(jù),我現(xiàn)在就看看React界面是否需要需要發(fā)生變化,啊,需要把新建預約的表單顯示出來啊,那界面還是要更新一下的,交給你了,React。
React:好的,有新的數(shù)據(jù)由store通過props傳遞下來的數(shù)據(jù)了,我會馬上根據(jù)這個數(shù)據(jù)把新建預約的表單顯示出來。
用戶:看到了新建預約的表單。
如果覺得還不錯,來個star吧。(笑臉)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/86631.html
摘要:項目地址下載完項目然后即可基于的項目,主要是為了學習實戰(zhàn)。數(shù)據(jù)都是固定的,從餓了么接口臨時抓的,模擬了一個的異步數(shù)據(jù)延遲,感謝餓了么。詳細信息可以看上面的官方文檔,我這里就簡單說一下我這個項目的應(yīng)用。 react-ele-webapp 項目地址 :https://github.com/kliuj/reac... run 下載完項目npm install然后npm run dev 即可 ...
摘要:但是和一起使用還需要一個工具,這一篇就說一下在使用上的一些性能優(yōu)化建議。如果的改變會引起值變化,那么會調(diào)用轉(zhuǎn)換函數(shù),傳入作為參數(shù),并返回結(jié)果。如果的值和前一次的一樣,它將會直接返回前一次計算的數(shù)據(jù),而不會再調(diào)用一次轉(zhuǎn)換函數(shù)。 前面寫了兩篇文章《React組件性能優(yōu)化》《Redux性能優(yōu)化》,分別針對React和Redux在使用上的性能優(yōu)化給了一些建議。但是React和Redux一起使用...
摘要:上一篇也許是最佳小實踐加入在組件之間流通數(shù)據(jù)更確切的說,這被叫做單向數(shù)據(jù)流數(shù)據(jù)沿著一個方向從父組件流到子組件。這個將這個新的對象附加到上,并將它返回,用來更新。這一次,將當前的狀態(tài)仍舊是空數(shù)組和對象一起傳遞給了。 上一篇:react、react-router、redux 也許是最佳小實踐1 加入 redux React 在組件之間流通數(shù)據(jù).更確切的說,這被叫做單向數(shù)據(jù)流——數(shù)據(jù)沿著一個...
摘要:簡介創(chuàng)建的函數(shù),返回一個對象,包含等方法合并多個中間件處理,在實際的前調(diào)用一系列中間件,類似于綁定和函數(shù)式編程中常見的方法,介紹官方提供的綁定庫。 前言 在學習了React之后, 緊跟著而來的就是Redux了~ 在系統(tǒng)性的學習一個東西的時候, 了解其背景、設(shè)計以及解決了什么問題都是非常必要的。接下來記錄的是, 我個人在學習Redux時的一些雜七雜八~ Redux是什么 通俗理解 h...
閱讀 3192·2021-11-23 10:09
閱讀 2057·2021-10-26 09:51
閱讀 975·2021-10-09 09:44
閱讀 3898·2021-10-08 10:04
閱讀 2742·2021-09-22 15:14
閱讀 3619·2021-09-22 15:02
閱讀 1035·2021-08-24 10:03
閱讀 1719·2019-12-27 12:14