Conversation
| <Layout> | ||
| <Wrapper> | ||
| {this.state.isLoading && <Loader />} | ||
| {this.props.product && ( |
There was a problem hiding this comment.
Since we are passing the product through the props now, it makes no sense to keep a single boolean value in the state for loading. Instead, it makes more sense to do something like:
{!this.props.product ? <Loader /> : <>The Actual product rendering</>}
As with the current implementation, we could potentially run into a condition where we display both the product detail and the loader.
| <Layout> | ||
| <Wrapper> | ||
| {this.state.isLoading && <Loader />} | ||
| {this.props.product && ( |
There was a problem hiding this comment.
There is a lot of this.props.product how about adding more destructuring for improved readability?
| } | ||
|
|
||
| async componentDidMount() { | ||
| if (this.props.products.length === 0) { |
There was a problem hiding this comment.
Do we want to remove this if?
I presume that since it was shown during the presentation a lot of students will keep it in their code and it will pass the HW review (unless we want to force mentors to make sure they throw it out) and suddenly we have different functionality for other presenters when presenting.
If we want to remove this part I would recommend doing that during the lesson or announcing it in the Slack channel, so we don't run into sneaky bugs due to this.
There was a problem hiding this comment.
We do, because otherwise if we load product detail page first, and then navigate to product list, we'll have one product to the store and the full list won't be loaded.
There was a problem hiding this comment.
Good point, we should mention that in Slack then so students aren't surprised by this.
There was a problem hiding this comment.
We can mention that we showed this as a sample of caching thanks to global state, yet for a more production ready setup you would need a more complex implementation.
* Add create-customer API call * Implement sign up form with styles and libraries * General layout improvement * ESLint and stylelint configuration fixes
dannytce
left a comment
There was a problem hiding this comment.
Overall great job! :) But I found a couple of things I would like to address too. Btw @varholak-peter had great point too!
Some of the issues are written as comments per line. Some of them I am going address here:
- in ProductDetail we changed to logic how to display
<Loader />. Let's use the same logic for ProductList - Let's rename
store/cartItemsintostore/cart - Let's use in store
reducer.jsinstead ofindex.js - It's not related to this PR, but we can probably address it here. There are exactly same data transformation in
get-products.jsandget-product.js. So first of all, can we have justproducts.jsorproducts/index.jshaving both functionalites there? Secondly, can we abstract those data transforms into helper function? :)
Otherwise I really like extra stuff you did. You are correct it's not related to the homework, but could be beneficial for our students. Let's communicate that in Slack! :)
| }) | ||
|
|
||
| const Cart = connect(mapStateToProps)(CartView) | ||
| const actionCreators = { |
There was a problem hiding this comment.
It's actually mapDispatchToProps. Naming doesn't matter here at all, but for students, it might be better for studying purposes... Because it actually does the mapping of dispatch to props :)
Also, the official documentation is using the same term :)
https://react-redux.js.org/next/api/connect#connect-parameters
There was a problem hiding this comment.
I think the documentation says that actionCreators is also an acceptable name here, since we don't do the actual "mapping" as we would with a more verbose syntax.
Since the actual name of the variable is up to you, you might want to give it a name like actionCreators, or even define the object inline in the call to connect
There was a problem hiding this comment.
Haha, fair point! :P But still, but still I am more into mapDispatchToProps :D
| <AddButton onClick={evt => onAddToCart(node.id, evt)}> | ||
| Add to Cart | ||
| </AddButton> | ||
| <Button onClick={evt => onAddToCart(node.id, evt)}>Add to Cart</Button> |
There was a problem hiding this comment.
Actually, let's make it more future proofed. The correct behavior supposed to be that onAddToCart shouldn't deal with anything else than the actual adding product to cart. Thus, let's have it as following:
<Button onClick={event => { event.preventDefault(); onAddToCart(node.id) }}>Add to Cart</Button>- This is a bit better because parent component doesn't have to care about the implementation.
- Please try to avoid using shortcuts for common parameters, such as
event. It's straightforward for us, but again confusing for students.
| const store = createStore(reducer) | ||
| const store = createStore( | ||
| reducer, | ||
| window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() |
There was a problem hiding this comment.
Let's have a comment there explaining where we are getting those devtools from :)
I think it's totally fine just to have there a link to: https://github.com/zalmoxisus/redux-devtools-extension#11-basic-store
| @@ -1,6 +1,12 @@ | |||
| export const LOAD_PRODUCTS = 'products/LOAD' | |||
| export const LOAD_PRODUCT = 'products/LOAD_ONE' | |||
There was a problem hiding this comment.
This is not good for debugging. Let's be more explicit by using the same names of constants
export const LOAD_PRODUCTS = 'products/LOAD_PRODUCTS'
export const LOAD_PRODUCT = 'products/LOAD_PRODUCT'
There was a problem hiding this comment.
Might be a little late, but could we change the naming LOAD_PRODUCT/S to SET_PRODUCT/S? I was confused when reading my student code that he was calling the API and then calling loadProducts, this word LOAD usually implies side effects so is more related to fetching in my opinion than using SET
| case LOAD_PRODUCTS: | ||
| return action.payload | ||
| case LOAD_PRODUCT: { | ||
| const restProducts = state.filter(p => p.id !== action.payload.id) |
There was a problem hiding this comment.
Maybe let's write it more clean and easier to follow?
return state.map(product => product.id === action.payload.id ? action.payload : product)Also, an explanation left in a comment, why we did this would be awesome. I assume we are doing that, because of the following:
- There might be already some products loaded (e.g. from product list)
- get product detail returns more data than product list
There was a problem hiding this comment.
The mapping logic won't work if the original state is empty. But I'll add an explanatory comment, that's a great idea!
| ...state, | ||
| [action.payload]: (state[action.payload] || 0) + 1, | ||
| } | ||
| case REMOVE_PRODUCT: { |
There was a problem hiding this comment.
Shouldn't this first decrease the quantity before getting removed? Maybe you just want to remove the duplicate that you accidentally added
There was a problem hiding this comment.
@Zaggen you are absolutely right! But let's keep this functionality to the next lessons.
Hi guys,
while doing this homework, I realized that there are a few things that I should probably include as part of the work that I did during the lecture, because some of the changes aren't really related to the homework itself. For instance, I want to prepare the following things so that students wouldn't have to worry about them:
The aforementioned changes are in this PR right now, but I think that I should make them myself so that students could build on top of them. What do you think?