Skip to content

Commit

Permalink
Retrieving Data from the Backend and Resetting Error Handler inside
Browse files Browse the repository at this point in the history
withErrorHandler Closure HOC inside the constructor()
  • Loading branch information
Ch-sriram committed Aug 7, 2020
1 parent ed494e3 commit 2593115
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 78 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@ Firebase Common API Endpoint: <https://burger-builder-ram.firebaseio.com/>
2. Creating the `axios` instance to access our DB: [Commit Details](https://github.com/Ch-sriram/burger-builder/commit/beb5dcb669a15a69b733f8b289e04ab5f06cdcdd)
3. Sending a POST Request using `axios.post()`: [Commit Details](https://github.com/Ch-sriram/burger-builder/commit/2c7645a8b26cd5a3db6ac0052e274cf89662b4ca)
4. Displaying a **[Spinner](https://projects.lukehaas.me/css-loaders/)** while sending a POST Request: [Commit Details](https://github.com/Ch-sriram/burger-builder/commit/2067df848d89f1b97b5d3564215b648d398468e0)
5. Handling Errors Globally using the `<Modal />` Component inside the `withErrorHandler` Closure HOC: [Commit Details](https://github.com/Ch-sriram/burger-builder/commit/74552c827692cadf1ef419214f360494ad350d81)
5. Handling Errors Globally using the `<Modal />` Component inside the `<withErrorHandler />` Closure HOC: [Commit Details](https://github.com/Ch-sriram/burger-builder/commit/74552c827692cadf1ef419214f360494ad350d81)
6. Retrieving Data from the **[Firebase Backend](https://burger-builder-ram.firebaseio.com/ingredients)** & Resetting Error Handler inside `withErrorHandler` Closure HOC inside the `constructor()` lifecycle method: [Commit Details]()
194 changes: 133 additions & 61 deletions src/containers/BurgerBuilder/BurgerBuilder.component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,131 +19,203 @@ const INGREDIENT_PRICES = {
bacon: 0.7
}

/**
* We can get the ingredients dynamically from the firebase DB,
* where we define the ingredients object (route) in the
* firebase DB, and fetch it in here, in componentDidMount()
* lifecycle method. We will fetch the default ingredients from
* the backend database.
*/

class BurgerBuilder extends Component {
state = {
ingredients: {
salad: 0,
bacon: 0,
cheese: 0,
meat: 0
},
ingredients: null,
totalPrice: 4,
purchasable: false,
orderNow: false,
loading: false,
error: false,
};

/**
* Fetch the default ingredients data from the DB here:
* https://burger-builder-ram.firebaseio.com/ingredients
*
* NOTE: we have to append `.json` at the end of the API
* endpoint in case of firebase db.
*/
componentDidMount() {

/**
* Now, if we deliberately omitted `.json` in the end, then
* in the view, the spinner will just keep on spinning
* infinitely.
*
* To correct that infinite spinning, we have to correct
* the code in the withErrorHandler Closure HOC, which
* runs the axios.interceptors inside componentDidMount()
* lifecycle method, which won't be mounted at all, when
* the error in here, is not fixed at all. And so,
* the component in question, which is the WrappedComponent
* inside the withErrorHandler, which is this current
* BurgerBuilder component, won't be able to handle the
* error, as here also, the componentDidMount() lifecycle
* method handles the error, and therefore, the .catch()
* method, also has to be defined here, so that, the error
* will be handled properly.
*/

// NOTE that `.json` is omitted to introduce an error deliberately
axios.get("https://burger-builder-ram.firebaseio.com/ingredients")
.then(response => {
this.setState({ ingredients: response.data },
() => {
const purchasableInfo = { ...this.state.ingredients };
for (let key in purchasableInfo) {
if (this.state.purchasable) {
break;
}
if (purchasableInfo[key] > 0) {
this.setState({ purchasable: true });
}
}
});
})
.catch(error => {
this.setState({ error: true }, () => console.log(error));
return error;
});
}

updatePurchasableState() {
const ingredients = { ...this.state.ingredients };
const sum = Object.keys(ingredients)
.map(ingredient => ingredients[ingredient])
.map((ingredient) => ingredients[ingredient])
.reduce((currSum, el) => currSum + el, 0);
this.setState({ purchasable: sum > 0 });
}

addIngredientHandler = type => {
addIngredientHandler = (type) => {
const oldCount = this.state.ingredients[type];
const updatedCount = oldCount + 1;
const updatedIngredients = { ...this.state.ingredients };
updatedIngredients[type] = updatedCount;

const priceAddition = INGREDIENT_PRICES[type];
const oldPrice = this.state.totalPrice;
const newPrice = oldPrice + priceAddition;

this.setState({
totalPrice: newPrice,
ingredients: updatedIngredients
}, () => {this.updatePurchasableState()});
this.setState(
{
totalPrice: newPrice,
ingredients: updatedIngredients,
},
() => {
this.updatePurchasableState();
}
);
};

removeIngredientHandler = type => {
removeIngredientHandler = (type) => {
const oldCount = this.state.ingredients[type];
if (oldCount <= 0) return;
const updatedCount = oldCount - 1;
const updatedIngredients = { ...this.state.ingredients };
updatedIngredients[type] = updatedCount;

const priceDeduction = INGREDIENT_PRICES[type];
const oldPrice = this.state.totalPrice;
const newPrice = oldPrice - priceDeduction;

this.setState({
totalPrice: newPrice,
ingredients: updatedIngredients
}, () => {this.updatePurchasableState()});
this.setState(
{
totalPrice: newPrice,
ingredients: updatedIngredients,
},
() => {
this.updatePurchasableState();
}
);
};

orderNowHandler = () => {
this.setState({ orderNow: true });
}
};

orderCancelHandler = () => {
this.setState({ orderNow: false });
}
};

orderContinueHandler = () => {
this.setState({ loading: true },
() => {
const order = {
ingredients: this.state.ingredients,
price: this.state.totalPrice,
customer: {
name: "Ch. Sriram",
address: {
street: "Crazy Street",
zipCode: "51251",
country: "Zambia"
},
email: "test@crazy.com",
deliveryMethod: "fastest"
}
}

// We deliberately omit the `.json` in the route to
// introduce an error, to see how errors pan out.
axios.post('/orders', order)
.then(response => {
this.setState({ loading: false, orderNow: false });
})
.catch(error => {
this.setState({ loading: false, orderNow: false });
});
this.setState({ loading: true }, () => {
const order = {
ingredients: this.state.ingredients,
price: this.state.totalPrice,
customer: {
name: "Ch. Sriram",
address: {
street: "Crazy Street",
zipCode: "51251",
country: "Zambia",
},
email: "test@crazy.com",
deliveryMethod: "fastest",
},
};

axios
.post("/orders.json", order)
.then((response) => {
this.setState({ loading: false, orderNow: false });
})
.catch((error) => {
this.setState({ loading: false, orderNow: false });
});
});
}
};

render() {
const disabledInfo = { ...this.state.ingredients };
for (let key in disabledInfo) {
disabledInfo[key] = disabledInfo[key] <= 0;
}

let orderSummary = this.state.loading ?
<Spinner /> : (
let orderSummary = null,
burger = this.state.error ? <p style={{textAlign: 'center'}}>Ingredients can't be loaded!</p> : <Spinner />;

if (this.state.ingredients) {
burger = (
<Wrapper>
<Burger ingredients={this.state.ingredients} />
<BuildControls
ingredientAdded={this.addIngredientHandler}
ingredientRemoved={this.removeIngredientHandler}
disabled={disabledInfo}
purchasable={this.state.purchasable}
price={this.state.totalPrice}
ordered={this.orderNowHandler}
/>
</Wrapper>
);

orderSummary = (
<OrderSummary
ingredients={this.state.ingredients}
price={this.state.totalPrice}
orderCancelled={this.orderCancelHandler}
orderContinued={this.orderContinueHandler}
/>
);
}

orderSummary = this.state.loading ? <Spinner /> : orderSummary;

return (
<Aux>
<Modal show={this.state.orderNow} modalClosed={this.orderCancelHandler}>
{orderSummary}
</Modal>
<Wrapper>
<Burger ingredients={this.state.ingredients} />
<BuildControls
ingredientAdded={this.addIngredientHandler}
ingredientRemoved={this.removeIngredientHandler}
disabled={disabledInfo}
purchasable={this.state.purchasable}
price={this.state.totalPrice}
ordered={this.orderNowHandler}
/>
</Wrapper>
{burger}
</Aux>
);
}
Expand Down
57 changes: 41 additions & 16 deletions src/hoc/withErrorHandler/withErrorHandler.closureHOC.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,55 @@ import Aux from '../Auxiliary/Auxiliary.hoc';

const withErrorHandler = (WrappedComponent, axios) => {
return class extends Component {
state = {
error: null,
}
constructor(props) {
super(props);
this.state = { error: null, };

/**
* We can also make this a functional component and use
* useEffect() hook instead of componentDidMount() method
* to handle global axios interceptors for error handling.
*/
componentDidMount() {
axios.interceptors.request.use(req => {
// we want to get rid of any previous errors whenever
// we want to get rid of any previous errors whenever
// we send our request
this.setState({ error: null });
return req;
})
});

axios.interceptors.response.use(res => res, error => {
// after getting the response, if there's an error,
// we'll simply handle it as follows:
this.setState({ error: error });
return Promise.reject(error);
})
// after getting the response, if there's an error,
// we'll simply handle it as follows:
this.setState({ error: error });
return Promise.reject(error);
}
);
}


/**
* Now, componentDidMount() is only called once the child
* components are rendered, but whenever an error occurs,
* we need it to be handled even if the child components
* are going to render. And for that, the better way of
* handling errors during axios requests/responses will
* be inside the componentWillUpdate() lifecycle method,
* and as it is deprecated now, we can handle the axios
* related errors inside the constructor() lifecycle hook.
*
* And so, we will comment the code below, and update the
* constructor() method as shown above.
*/
// componentDidMount() {
// axios.interceptors.request.use(req => {
// // we want to get rid of any previous errors whenever
// // we send our request
// this.setState({ error: null });
// return req;
// })
// axios.interceptors.response.use(res => res, error => {
// // after getting the response, if there's an error,
// // we'll simply handle it as follows:
// this.setState({ error: error });
// return Promise.reject(error);
// })
// }

errorConfirmedHandler = () => {
this.setState({ error: null });
}
Expand Down

0 comments on commit 2593115

Please sign in to comment.