在index中加入redux dev-tools
import burgerBulderReducer from './store/reducers/burgerBuilder';
const store =createStore(burgerBulderReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
将Action转为Action Creators
将store中的reducer和action分为actions和reucers;
burgerBuilder.js
存放ingredients的加减action creators;
import * as actionTypes from './actionsTypes';
export const addIngredient = (name)=>{ //转为action creator
return {
type:actionTypes.ADD_INGREDIENTS,
ingredientName: name
};
};
export const removeIngredient = (name)=>{
return {
type:actionTypes.REMOVE_INGREDIENTS,
ingredientName:name
};
};
index.js
将所有需要export的action creator在此export;
export {addIngredient, removeIngredient} from './burgerBuilder';
export {} from './order';
在BurgerBuilder.js中的dispatch中,调用此action creator
import * as burgerBuilderActions from '../../store/actions/index';
const mapDispatchToProps=dispatch=>{
return{
onIngredientAdded:(ingName)=>dispatch(burgerBuilderActions.addIngredient(ingName)),
onIngredientRemoved:(ingName)=>dispatch(burgerBuilderActions.removeIngredient(ingName))
};
};
由此——同步的actions已转为actioncreators
为action添加异步操作
将dev-tools调整为advanced mode
import {createStore,applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store =createStore(burgerBulderReducer, composeEnhancers(
applyMiddleware(thunk)
));
在componentdidmount中调用异步请求
BurgerBuilder——get请求
actionType中建立两个新的actiontypes
export const ADD_INGREDIENTS = 'ADD_INGREDIENTS';
export const REMOVE_INGREDIENTS='REMOVE_INGREDIENTS';
export const SET_INGREDIENTS = 'SET_INGREDIENTS';
export const FETCH_INGREDIENTS_FAILED='FETCH_INGREDIENTS_FAILED';
reducer中设置这两个type的case
第一个将ingredients放入state;
第二个将state中的error变为true,说明请求失败;
case actionTypes.SET_INGREDIENTS:
return {
...state,
ingredients:action.ingredients,
error:false
};
case actionTypes.FETCH_INGREDIENTS_FAILED:
return {
...state,
error:true
};
设置请求的action creator
同步函数+异步函数(middleware辅助);
在axiosget成功后进行dispatch;
若失败则也dispatch;
export const setIngredients =(ingredients)=>{
return {
type:actionTypes.SET_INGREDIENTS,
ingredients:ingredients
};
};
export const initIngredients=()=>{
return (dispatch)=>{ //thunk使得dispatch可被传入
axios.get( '/ingredients.json' )
.then( response => {
dispatch(setIngredients(response.data));
} )
.catch( error => {
dispatch(fetchingredientsfailed());
} );
};
};
最后在BurgerBuilder的didimount中调用此异步函数
componentDidMount () { //用于从database抓取初始ingredient
this.props.onInitIngredients();
}
Order & Checkout——post请求
actionTypes.js
success用与异步action的调用;
start用于判断异步时loading的状态;
export const ADD_INGREDIENTS = 'ADD_INGREDIENTS';
export const REMOVE_INGREDIENTS='REMOVE_INGREDIENTS';
export const SET_INGREDIENTS = 'SET_INGREDIENTS';
export const FETCH_INGREDIENTS_FAILED='FETCH_INGREDIENTS_FAILED';
export const PURCHASE_BURGER_SUCCESS = 'PURCHASE_BURGER_SUCCESS';
export const PURCHASE_BURGER_FAIL = 'PURCHASE_BURGER_FAIL';
//控制loading
export const PURCAHSE_BURGER_START ='PURCAHSE_BURGER_START';
Order——action creator
成功,失败,开始都在异步中被调用;
reducer中设置同步action的case;
import * as actionType from './actionsTypes';
import axios from '../../axios-orders';
export const purchaseBurgerSuccess =(id, orderData)=>{
return {
type:actionType.PURCHASE_BURGER_SUCCESS,
orderId:id,
orderData:orderData
};
};
export const purchaseBurgerFail = (error)=>{
return {
type:actionType.PURCHASE_BURGER_FAIL,
error:error
};
};
export const purchaseBurgerStart=()=>{ //用于在reducer中控制loading状态
return {
type:actionType.PURCAHSE_BURGER_START
}
}
//async 不返回一个action,返回一个调用action的异步函数
export const purchaseBurger = (orderData) => {
return dispatch=>{
dispatch(purchaseBurgerStart());//在异步调用前改变loading——要用dispatch包裹
axios.post( '/orders.json', orderData )
.then( response => {
console.log(response.data); //name才是真正的id
dispatch(purchaseBurgerSuccess(response.data.name,orderData));
} )
.catch( error => {
dispatch(purchaseBurgerFail(error));
} );
};
};
Order——reducer
成功则改变状态;
失败则保持状态;
开始则改变laoding状态;
import * as actionTypes from '../actions/actionsTypes';
const initialState={
orders: [],
loading: false
};
const reducer = (state=initialState, action)=>{
switch(action.type){
case actionTypes.PURCHASE_BURGER_SUCCESS:
const newOrder ={
...action.orderData,
id:action.orderId
};
return{
...state,
loading: false,
orders: state.orders.concat(newOrder)
};
case actionTypes.PURCHASE_BURGER_FAIL:
return{
...state,
loading:false //不改变state,用witherror获取error
};
case actionTypes.PURCAHSE_BURGER_START:
return{
...state,
loading:true
};
default:
return state;
}
};
export default reducer;
放入checkout和contactdata中
import * as actions from '../../store/actions/index';
提交表单——处理order,传入异步action
orderHandler=(event)=>{ //接受点击事件
const formData = {};
for(let formElementIdentifier in this.state.orderForm){
formData[formElementIdentifier] = this.state.orderForm[formElementIdentifier].value;
};
const order = {
ingredients: this.props.ings,
price: this.props.price,
orderData:formData
}
this.props.onOrderBurger(order); //调用action
event.preventDefault(); //阻止页面刷新
}
error可用witherrorhandler捕获,传入reducer的状态和action;
const mapStateToProps = state=>{
return{
ings:state.burgerBuilder.ingredients,
price:state.burgerBuilder.totalPrice,
loading:state.order.loading
}
};
const mapDispatchToProps = dispatch=>{
return{
onOrderBurger:(orderData)=>dispatch(actions.purchaseBurger(orderData))
};
}
export default connect(mapStateToProps,mapDispatchToProps)(withErrorHandler(ContactData,axios));
将两个reducer合体——再index.js中
用到 combineReducers from ‘redux’
import React, { createContext } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import {BrowserRouter} from 'react-router-dom';
import {Provider} from 'react-redux';
import {createStore,applyMiddleware, compose, combineReducers} from 'redux';
import thunk from 'redux-thunk';
import burgerBulderReducer from './store/reducers/burgerBuilder';
import orderReducer from './store/reducers/order';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const rootReducer=combineReducers({
burgerBuilder:burgerBulderReducer,
order:orderReducer
});
const store =createStore(rootReducer, composeEnhancers(
applyMiddleware(thunk)
));
ReactDOM.render(
<Provider store = {store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
registerServiceWorker();
调用state.burgerbuiilder.statename或state.order.statename
Bug——提交后重定向+重置价格
重定向——BurgerBuilder.js中
按下continue后就将purchased定位false;
在进入checkout页面之前,将state转为false;
purchaseContinueHandler = () => { //重定位在这里
this.props.onInitPurchase();
this.props.history.push('/checkout');
}
const mapDispatchToProps=dispatch=>{
return{
onIngredientAdded:(ingName)=>dispatch(Actions.addIngredient(ingName)),
onIngredientRemoved:(ingName)=>dispatch(Actions.removeIngredient(ingName)),
onInitIngredients:()=>dispatch(Actions.initIngredients()),
onInitPurchase: ()=>dispatch(Actions.purchaseInit())
};
};
若将函数放在checkout的 willmount中,不能阻止一开始render true状态,依旧会重定向到’/’;
所以需要放在checkout的前一个component中,在进入checkoutcomponent之前将state 变为false;
Price重置
case actionTypes.SET_INGREDIENTS:
return {
...state,
ingredients:action.ingredients,
error:false,
totalPrice:3.7
};
actions由action中的inedx.js集中存放
reducers由combineReducers合并
用redux调用order界面
与一开始getingredients基本相同
异步分为success,failed,start三个同步action;
export const FETCH_ORDERS_START = 'FETCH_ORDERS_START';
export const FETCH_ORDERS_SUCCESS = 'FETCH_ORDERS_SUCCESS';
export const FETCH_ORDERS_FAIL = 'FETCH_ORDERS_FAIL';
同步+异步action
先start,then(success),catch(failed)
//orders success+fail+asych
export const fetchOrdersSuccess=(orders)=>{
return {
type:actionType.FETCH_ORDERS_SUCCESS,
orders:orders
};
};
export const fetchOrdersFailed=(error)=>{
return {
type:actionType.FETCH_ORDERS_FAIL,
error:error
};
};
export const fetchOrdersStart=()=>{
return{
type:actionType.FETCH_ORDERS_START
};
};
export const fetchOrders= ()=>(dispatch)=>{ //actioncreator函数——返回一个action
dispatch(fetchOrdersStart());
axios.get('/orders.json')
.then(res=>{
const fetchOrders=[];
for(let key in res.data){
fetchOrders.push({
...res.data[key],
id:key
});
}
dispatch(fetchOrdersSuccess(fetchOrders));
})
.catch(error=>{
dispatch(fetchOrdersFailed(error));
});
};
在reducer中添加三个case
};
case actionTypes.FETCH_ORDERS_START:
return{
...state,
loading:true //一个参数用多个页面
};
case actionTypes.FETCH_ORDERS_SUCCESS:
return{
...state,
orders:action.orders,
loading:false
};
case actionTypes.FETCH_ORDERS_FAIL:
return {
...state,
loading:false
}
将异步action放入orders.js中
connect传入state和action;
spinner配合条件判断,在start和success中间显示spinner;
import React, { Component } from 'react';
import Order from '../../components/order/Order';
import axios from '../../axios-orders';
import withErrorHandler from '../../hoc/withErrorHandler/withErrorHandler';
import * as actions from '../../store/actions/index'; //index可省略
import {connect} from 'react-redux';
import Spinner from '../../components/UI/Spinner/Spinner';
class Orders extends Component{
//fetch all orders
componentDidMount(){
this.props.onFetchOrders();
};
render(){
let order = <Spinner/>;
if(!this.props.loading){
order=(
this.props.orders.map((order)=>(
<Order
key ={order.id}
ingredients={order.ingredients}
price={order.price}
/>
)
));
};
return(
<div>
{order}
</div>
);
};
};
const mapStateToProps = state=>{
return{
orders:state.order.orders,
loading: state.order.loading
};
};
const mapDispatchToProps = dispatch => {
return {
onFetchOrders: ()=>dispatch(actions.fetchOrders())
};
};
export default connect(mapStateToProps,mapDispatchToProps)(withErrorHandler(Orders, axios));