From 1988b97700f32fe180e35bc63756cbaac083fd79 Mon Sep 17 00:00:00 2001 From: Alexey Date: Fri, 29 Oct 2021 07:42:41 +0300 Subject: [PATCH 1/2] HT-5 kostenevich --- src/components/menu/menu.js | 27 +- src/components/restaurant/restaurant.js | 2 +- src/components/reviews/review/review.js | 39 +-- src/components/reviews/reviews.js | 56 +++- src/fixtures.js | 334 ------------------------ src/redux/actions.js | 28 ++ src/redux/constants.js | 2 + src/redux/middleware/api.js | 1 - src/redux/middleware/logger.js | 6 +- src/redux/reducer/products.js | 40 ++- src/redux/reducer/reviews.js | 55 +++- src/redux/reducer/users.js | 38 ++- src/redux/selectors.js | 50 +++- 13 files changed, 283 insertions(+), 395 deletions(-) delete mode 100644 src/fixtures.js diff --git a/src/components/menu/menu.js b/src/components/menu/menu.js index 4ee1ded..9d22927 100644 --- a/src/components/menu/menu.js +++ b/src/components/menu/menu.js @@ -5,6 +5,13 @@ import Product from '../product'; import Basket from '../basket'; import styles from './menu.module.css'; +import { connect } from 'react-redux'; +import { loadProducts } from '../../redux/actions'; +import { + productLoadedSelector, + productLoadingSelector, +} from '../../redux/selectors'; +import Loader from '../loader'; class Menu extends Component { static propTypes = { @@ -13,11 +20,19 @@ class Menu extends Component { state = { error: null }; + componentDidMount() { + if (!this.props.loading && !this.props.loaded) + this.props.loadProductList(this.props.restId); + } + componentDidCatch(error) { this.setState({ error }); } render() { + if (this.props.loading) return ; + if (!this.props.loaded) return 'No data :('; + const { menu } = this.props; if (this.state.error) { @@ -39,4 +54,14 @@ class Menu extends Component { } } -export default Menu; +const mapStateToProps = (state, props) => ({ + loading: productLoadingSelector(state, props), + loaded: productLoadedSelector(state, props), + restId: props.restId, +}); + +const mapDispatchToProps = (dispatch) => ({ + loadProductList: (restId) => dispatch(loadProducts(restId)), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Menu); diff --git a/src/components/restaurant/restaurant.js b/src/components/restaurant/restaurant.js index 51fe01e..8cb7a7f 100644 --- a/src/components/restaurant/restaurant.js +++ b/src/components/restaurant/restaurant.js @@ -27,7 +27,7 @@ const Restaurant = ({ restaurant, averageRating }) => { - {activeTab === 'menu' && } + {activeTab === 'menu' && } {activeTab === 'reviews' && } ); diff --git a/src/components/reviews/review/review.js b/src/components/reviews/review/review.js index a84c9f0..b6cfc7a 100644 --- a/src/components/reviews/review/review.js +++ b/src/components/reviews/review/review.js @@ -6,23 +6,25 @@ import styles from './review.module.css'; import { reviewWitUserSelector } from '../../../redux/selectors'; -const Review = ({ user, text, rating }) => ( -
-
-
-

- {user} -

-

- {text} -

-
-
- +const Review = ({ user, text, rating }) => { + return ( +
+
+
+

+ {user} +

+

+ {text} +

+
+
+ +
-
-); + ); +}; Review.propTypes = { user: PropTypes.string, @@ -36,9 +38,14 @@ Review.defaultProps = { // const mapStateToProps = (state, props) => ({ // ...reviewWitUserSelector(state, props), +// loading: reviewLoadingSelector(state, props), +// loaded: reviewLoadedSelector(state, props), +// restId: props.restId, // }); -// const mapStateToProps = (state, props) => reviewWitUserSelector(state, props); +// const mapDispatchToProps = (dispatch, props) => ({ +// loadReviews: () => dispatch(loadReviews(props.restId)), +// }); const mapStateToProps = reviewWitUserSelector; diff --git a/src/components/reviews/reviews.js b/src/components/reviews/reviews.js index 8dc5628..8630dd7 100644 --- a/src/components/reviews/reviews.js +++ b/src/components/reviews/reviews.js @@ -5,17 +5,45 @@ import Review from './review'; import ReviewForm from './review-form'; import styles from './reviews.module.css'; -import { loadReviews } from '../../redux/actions'; +import { loadReviews, loadUsers } from '../../redux/actions'; +import { + reviewLoadedSelector, + reviewLoadedUsers, + reviewLoadingSelector, + reviewLoadingUsers, +} from '../../redux/selectors'; +import Loader from '../loader'; -const Reviews = ({ reviews, restId, loadReviews }) => { +const Reviews = ({ + reviews, + restId, + loadReviews, + loaded, + loading, + loadedUsers, + loadingUsers, + loadUsers, +}) => { useEffect(() => { - loadReviews(restId); - }, [restId, loadReviews]); + if (!loadedUsers && !loadingUsers) loadUsers(); + if (!loaded && !loading) loadReviews(restId); + }, [ + restId, + loadReviews, + loadUsers, + loaded, + loadedUsers, + loading, + loadingUsers, + ]); + + if (loading || loadingUsers) return ; + if (!loaded && !loadedUsers) return 'No data :('; return (
{reviews.map((id) => ( - + ))}
@@ -27,8 +55,20 @@ Reviews.propTypes = { reviews: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, }; -const mapDispatchToProps = { - loadReviews, +const mapStateToProps = (state, props) => { + console.log(props); + return { + restId: props.restId, + loadedUsers: reviewLoadedUsers(state), + loadingUsers: reviewLoadingUsers(state), + loaded: reviewLoadedSelector(state, props), + loading: reviewLoadingSelector(state, props), + }; }; -export default connect(null, mapDispatchToProps)(Reviews); +const mapDispatchToProps = (dispatch, props) => ({ + loadReviews: () => dispatch(loadReviews(props.restId)), + loadUsers: () => dispatch(loadUsers()), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Reviews); diff --git a/src/fixtures.js b/src/fixtures.js deleted file mode 100644 index ed35fc6..0000000 --- a/src/fixtures.js +++ /dev/null @@ -1,334 +0,0 @@ -export const restaurants = [ - { - id: 'a757a0e9-03c1-4a2a-b384-8ac21dbe2fb2', - name: 'Dishoom', - menu: [ - { - id: 'd75f762a-eadd-49be-8918-ed0daa8dd024', - name: 'Chicken tikka masala', - price: 12, - ingredients: ['chicken', 'rice'], - }, - { - id: 'c3cb8f92-a2ed-4716-92a1-b6ea813e9049', - name: 'Naan', - price: 3, - ingredients: ['bread'], - }, - { - id: 'bd129641-c0eb-432b-84b6-8b81d2930358', - name: 'Samosa', - price: 8, - ingredients: ['chicken', 'bread'], - }, - ], - reviews: [ - { - id: '5909796d-5030-4e36-adec-68b8f9ec2d96', - user: 'Antony', - text: 'Not bad', - rating: 5, - }, - { - id: '429dea85-11dd-4054-a31e-c60c92e17255', - user: 'Sam', - text: 'No burgers', - rating: 3, - }, - ], - }, - { - id: 'bb8afbec-2fec-491f-93e9-7f13950dd80b', - name: 'Homeslice', - menu: [ - { - id: '25402233-0095-49ea-9939-1e67ed89ffb9', - name: 'Margarita', - price: 9, - ingredients: ['bread', 'cheese', 'tomatoes'], - }, - { - id: '90902233-0095-49ea-9939-1e67ed89ffb9', - name: 'Chef pizza', - price: 10, - ingredients: ['bread', 'cheese', 'tomatoes', 'chicken'], - }, - ], - reviews: [ - { - id: '53b642d7-5e86-4717-a466-0640a1dee076', - user: 'Diana', - text: 'Perfect Margarita', - rating: 5, - }, - { - id: 'c27ab88e-375c-4e98-aa94-8a180150a797', - user: 'Sam', - text: 'No burgers again. But Chef Pizza is the best one', - rating: 4, - }, - { - id: 'abc0c5e1-cd57-4f0a-99d9-00e6b4533b3a', - user: 'Lolly', - text: 'Good for lunch', - rating: 5, - }, - ], - }, - { - id: '982bfbce-c5e0-41a0-9f99-d5c20ecee49d', - name: 'Fabrique', - menu: [ - { - id: '08c9ffa0-d003-4310-9e15-20978743296e', - name: 'Cinnamon buns', - price: 5, - ingredients: ['bread'], - }, - { - id: '64a4967c-2080-4a99-9074-4655a4569a95', - name: 'Semlor', - price: 2, - ingredients: ['bread', 'cream'], - }, - { - id: '4bc8528e-26d1-46c3-a522-8e18d10c8c84', - name: 'Saffron bun', - price: 4, - ingredients: ['bread'], - }, - ], - reviews: [ - { - id: '53b642d7-5e86-4717-a466-0640a1dee076', - user: 'Agata', - text: 'Best bakery', - rating: 5, - }, - ], - }, - { - id: 'd9241927-09e1-44f3-8986-a76346869037', - name: 'Flat Iron', - menu: [ - { - id: '6c02c2ce-b868-4191-b4a7-8686429f4bac', - name: 'Flat Iron Steak', - price: 10, - ingredients: ['beef'], - }, - { - id: '99bb6fbb-e53b-4b7e-b9c2-23b63b77385d', - name: 'Flat Iron Burger', - price: 10, - ingredients: ['bread', 'beef'], - }, - ], - reviews: [ - { - id: '5db6247b-ab1c-49db-be1f-8dd27fd38b81', - user: 'Sam', - text: 'Finally! This place is amazing place for breakfast, lunch, dinner and supper', - rating: 5, - }, - { - id: '381b0c31-6360-43ff-80d1-581a116159d8', - user: 'Rebeca', - text: 'Meat here is extremely delicious', - rating: 5, - }, - ], - }, -]; - -export const normalizedRestaurants = [ - { - id: 'a757a0e9-03c1-4a2a-b384-8ac21dbe2fb2', - name: 'Dishoom', - menu: [ - 'd75f762a-eadd-49be-8918-ed0daa8dd024', - 'c3cb8f92-a2ed-4716-92a1-b6ea813e9049', - 'bd129641-c0eb-432b-84b6-8b81d2930358', - ], - reviews: [ - '5909796d-5030-4e36-adec-68b8f9ec2d96', - '429dea85-11dd-4054-a31e-c60c92e17255', - ], - }, - { - id: 'bb8afbec-2fec-491f-93e9-7f13950dd80b', - name: 'Homeslice', - menu: [ - '25402233-0095-49ea-9939-1e67ed89ffb9', - '90902233-0095-49ea-9939-1e67ed89ffb9', - ], - reviews: [ - '53b642d7-5e86-4717-a466-0640a1dee076', - 'c27ab88e-375c-4e98-aa94-8a180150a797', - 'abc0c5e1-cd57-4f0a-99d9-00e6b4533b3a', - ], - }, - { - id: '982bfbce-c5e0-41a0-9f99-d5c20ecee49d', - name: 'Fabrique', - menu: [ - '08c9ffa0-d003-4310-9e15-20978743296e', - '64a4967c-2080-4a99-9074-4655a4569a95', - '4bc8528e-26d1-46c3-a522-8e18d10c8c84', - ], - reviews: ['13b642d7-5e86-4717-a466-0640a1dee076'], - }, - { - id: 'd9241927-09e1-44f3-8986-a76346869037', - name: 'Flat Iron', - menu: [ - '6c02c2ce-b868-4191-b4a7-8686429f4bac', - '99bb6fbb-e53b-4b7e-b9c2-23b63b77385d', - ], - reviews: [ - '5db6247b-ab1c-49db-be1f-8dd27fd38b81', - '381b0c31-6360-43ff-80d1-581a116159d8', - ], - }, -]; - -export const normalizedProducts = [ - { - id: 'd75f762a-eadd-49be-8918-ed0daa8dd024', - name: 'Chicken tikka masala', - price: 12, - ingredients: ['chicken', 'rice'], - }, - { - id: 'c3cb8f92-a2ed-4716-92a1-b6ea813e9049', - name: 'Naan', - price: 3, - ingredients: ['bread'], - }, - { - id: 'bd129641-c0eb-432b-84b6-8b81d2930358', - name: 'Samosa', - price: 8, - ingredients: ['chicken', 'bread'], - }, - { - id: '25402233-0095-49ea-9939-1e67ed89ffb9', - name: 'Margarita', - price: 9, - ingredients: ['bread', 'cheese', 'tomatoes'], - }, - { - id: '90902233-0095-49ea-9939-1e67ed89ffb9', - name: 'Chef pizza', - price: 10, - ingredients: ['bread', 'cheese', 'tomatoes', 'chicken'], - }, - { - id: '08c9ffa0-d003-4310-9e15-20978743296e', - name: 'Cinnamon buns', - price: 5, - ingredients: ['bread'], - }, - { - id: '64a4967c-2080-4a99-9074-4655a4569a95', - name: 'Semlor', - price: 2, - ingredients: ['bread', 'cream'], - }, - { - id: '4bc8528e-26d1-46c3-a522-8e18d10c8c84', - name: 'Saffron bun', - price: 4, - ingredients: ['bread'], - }, - { - id: '6c02c2ce-b868-4191-b4a7-8686429f4bac', - name: 'Flat Iron Steak', - price: 10, - ingredients: ['beef'], - }, - { - id: '99bb6fbb-e53b-4b7e-b9c2-23b63b77385d', - name: 'Flat Iron Burger', - price: 10, - ingredients: ['bread', 'beef'], - }, -]; - -export const normalizedReviews = [ - { - id: '5909796d-5030-4e36-adec-68b8f9ec2d96', - userId: 'a304959a-76c0-4b34-954a-b38dbf310360', - text: 'Not bad', - rating: 5, - }, - { - id: '429dea85-11dd-4054-a31e-c60c92e17255', - userId: 'dfb982e9-b432-4b7d-aec6-7f6ff2e6af54', - text: 'No burgers', - rating: 3, - }, - { - id: '53b642d7-5e86-4717-a466-0640a1dee076', - userId: '20bed9b5-9c7b-4771-8221-75b74ed1904a', - text: 'Perfect Margarita', - rating: 5, - }, - { - id: 'c27ab88e-375c-4e98-aa94-8a180150a797', - userId: 'dfb982e9-b432-4b7d-aec6-7f6ff2e6af54', - text: 'No burgers again. But Chef Pizza is the best one', - rating: 4, - }, - { - id: 'abc0c5e1-cd57-4f0a-99d9-00e6b4533b3a', - userId: 'c3d4abd4-c3ef-46e1-8719-eb17db1d6e99', - text: 'Good for lunch', - rating: 5, - }, - { - id: '13b642d7-5e86-4717-a466-0640a1dee076', - userId: '52a63cc0-5a6f-41f3-9774-0161ea4c9b0c', - text: 'Best bakery', - rating: 5, - }, - { - id: '5db6247b-ab1c-49db-be1f-8dd27fd38b81', - userId: 'dfb982e9-b432-4b7d-aec6-7f6ff2e6af54', - text: 'Finally! This place is amazing place for breakfast, lunch, dinner and supper', - rating: 5, - }, - { - id: '381b0c31-6360-43ff-80d1-581a116159d8', - userId: '1547335a-ea18-4547-a73d-32bd6e9f651c', - text: 'Meat here is extremely delicious', - rating: 5, - }, -]; - -export const normalizedUsers = [ - { - id: 'a304959a-76c0-4b34-954a-b38dbf310360', - name: 'Antony', - }, - { - id: '20bed9b5-9c7b-4771-8221-75b74ed1904a', - name: 'Diana', - }, - { - id: 'c3d4abd4-c3ef-46e1-8719-eb17db1d6e99', - name: 'Lolly', - }, - { - id: '52a63cc0-5a6f-41f3-9774-0161ea4c9b0c', - name: 'Agata', - }, - { - id: '1547335a-ea18-4547-a73d-32bd6e9f651c', - name: 'Rebeca', - }, - { - id: 'dfb982e9-b432-4b7d-aec6-7f6ff2e6af54', - name: 'Sam', - }, -]; diff --git a/src/redux/actions.js b/src/redux/actions.js index ae2a5c2..b8de1c2 100644 --- a/src/redux/actions.js +++ b/src/redux/actions.js @@ -9,6 +9,8 @@ import { SUCCESS, FAILURE, LOAD_REVIEWS, + LOAD_PRODUCTS, + LOAD_USERS, } from './constants'; export const increment = (id) => ({ type: INCREMENT, id }); @@ -32,6 +34,14 @@ export const loadRestaurants = () => ({ CallAPI: '/api/restaurants', }); +export const loadProducts = (restId) => { + return { + restId: restId, + type: LOAD_PRODUCTS, + CallAPI: `/api/products?id=${restId}`, + }; +}; + export const loadReviews = (restId) => async (dispatch) => { dispatch({ type: LOAD_REVIEWS + REQUEST, restId }); @@ -44,3 +54,21 @@ export const loadReviews = (restId) => async (dispatch) => { dispatch({ type: LOAD_REVIEWS + FAILURE, restId, error }); } }; + +// export const loadUsers = () => { +// return { +// type: LOAD_USERS, +// CallAPI: '/api/users', +// }; +// }; + +export const loadUsers = () => async (dispatch) => { + dispatch({ type: LOAD_USERS + REQUEST }); + + try { + const data = await fetch('/api/users').then((res) => res.json()); + dispatch({ type: LOAD_USERS + SUCCESS, data }); + } catch (error) { + dispatch({ type: LOAD_USERS + FAILURE, error }); + } +}; diff --git a/src/redux/constants.js b/src/redux/constants.js index ba58347..15285a9 100644 --- a/src/redux/constants.js +++ b/src/redux/constants.js @@ -6,6 +6,8 @@ export const CHANGE_RESTAURANT = 'CHANGE_RESTAURANT'; export const LOAD_RESTAURANTS = 'LOAD_RESTAURANTS'; export const LOAD_REVIEWS = 'LOAD_REVIEWS'; +export const LOAD_PRODUCTS = 'LOAD_PRODUCTS'; +export const LOAD_USERS = 'LOAD_USERS'; export const REQUEST = '_REQUEST'; export const SUCCESS = '_SUCCESS'; diff --git a/src/redux/middleware/api.js b/src/redux/middleware/api.js index da44ff0..61c229b 100644 --- a/src/redux/middleware/api.js +++ b/src/redux/middleware/api.js @@ -6,7 +6,6 @@ export default (store) => (next) => async (action) => { const { CallAPI, type, ...rest } = action; next({ ...rest, type: type + REQUEST }); - try { const data = await fetch(CallAPI).then((res) => res.json()); next({ ...rest, type: type + SUCCESS, data }); diff --git a/src/redux/middleware/logger.js b/src/redux/middleware/logger.js index d6ba2fd..9e6410e 100644 --- a/src/redux/middleware/logger.js +++ b/src/redux/middleware/logger.js @@ -1,6 +1,6 @@ export default (store) => (next) => (action) => { - // console.log('before: ', store.getState()); - // console.log('action: ', action); + console.log('before: ', store.getState()); + console.log('action: ', action); next(action); - // console.log('after: ', store.getState()); + console.log('after: ', store.getState()); }; diff --git a/src/redux/reducer/products.js b/src/redux/reducer/products.js index 55bc808..4124877 100644 --- a/src/redux/reducer/products.js +++ b/src/redux/reducer/products.js @@ -1,11 +1,41 @@ -import { normalizedProducts } from '../../fixtures'; +import produce from 'immer'; +import { FAILURE, LOAD_PRODUCTS, REQUEST, SUCCESS } from '../constants'; import { arrToMap } from '../utils'; -export default (state = arrToMap(normalizedProducts), action) => { - const { type } = action; +const initialState = { + entities: {}, + loaded: {}, + loading: {}, + error: {}, +}; + +export default produce((draft = initialState, action) => { + const { type, data, error, restId } = action; switch (type) { + case LOAD_PRODUCTS + REQUEST: + draft.loaded[restId] = false; + draft.loading[restId] = true; + draft.error[restId] = null; + return draft; + + case LOAD_PRODUCTS + SUCCESS: + draft.loaded[restId] = true; + draft.loading[restId] = false; + draft.error[restId] = null; + draft.entities = { + ...draft.entities, + ...arrToMap(data), + }; + return draft; + + case LOAD_PRODUCTS + FAILURE: + draft.loaded[restId] = false; + draft.loading[restId] = false; + draft.error[restId] = error; + return draft; + default: - return state; + return draft; } -}; +}); diff --git a/src/redux/reducer/reviews.js b/src/redux/reducer/reviews.js index e79ab91..ebe5925 100644 --- a/src/redux/reducer/reviews.js +++ b/src/redux/reducer/reviews.js @@ -1,18 +1,53 @@ -import { ADD_REVIEW } from '../constants'; -import { normalizedReviews } from '../../fixtures'; +import { + ADD_REVIEW, + FAILURE, + LOAD_REVIEWS, + REQUEST, + SUCCESS, +} from '../constants'; import { arrToMap } from '../utils'; +import produce from 'immer'; -export default (state = arrToMap(normalizedReviews), action) => { - const { type, review, reviewId, userId } = action; +const initialState = { + entities: {}, + loaded: {}, + loading: {}, + error: {}, +}; + +export default produce((draft = initialState, action) => { + const { type, data, error, restId, review, reviewId, userId } = action; switch (type) { + case LOAD_REVIEWS + REQUEST: + draft.loaded[restId] = false; + draft.loading[restId] = true; + draft.error[restId] = null; + return draft; + + case LOAD_REVIEWS + SUCCESS: + draft.loaded[restId] = true; + draft.loading[restId] = false; + draft.error[restId] = null; + draft.entities = { + ...draft.entities, + ...arrToMap(data), + }; + return draft; + + case LOAD_REVIEWS + FAILURE: + console.log(action); + draft.loaded[restId] = false; + draft.loading[restId] = false; + draft.error[restId] = error; + return draft; + case ADD_REVIEW: const { text, rating } = review; - return { - ...state, - [reviewId]: { id: reviewId, userId, text, rating }, - }; + draft.entities[reviewId] = { rating, text, userId, reviewId }; + return draft; + default: - return state; + return draft; } -}; +}); diff --git a/src/redux/reducer/users.js b/src/redux/reducer/users.js index 1143dff..1392801 100644 --- a/src/redux/reducer/users.js +++ b/src/redux/reducer/users.js @@ -1,15 +1,43 @@ import produce from 'immer'; -import { ADD_REVIEW } from '../constants'; -import { normalizedUsers } from '../../fixtures'; +import { + ADD_REVIEW, + FAILURE, + LOAD_USERS, + REQUEST, + SUCCESS, +} from '../constants'; import { arrToMap } from '../utils'; -export default produce((draft = arrToMap(normalizedUsers), action) => { - const { type, review, userId } = action; +const initialState = { + loaded: false, + loading: false, + entities: {}, + error: null, +}; + +export default produce((draft = initialState, action) => { + const { type, review, userId, data, error } = action; switch (type) { + case LOAD_USERS + REQUEST: + draft.loading = true; + return draft; + case LOAD_USERS + SUCCESS: + draft.loaded = true; + draft.loading = false; + draft.entities = { + ...draft.entities, + ...arrToMap(data), + }; + return draft; + case LOAD_USERS + FAILURE: + draft.loading = false; + draft.loaded = false; + draft.error = error; + return draft; case ADD_REVIEW: const { name } = review; - draft[userId] = { id: userId, name }; + draft.entities[userId] = { id: userId, name }; break; default: diff --git a/src/redux/selectors.js b/src/redux/selectors.js index 547712d..e9b8607 100644 --- a/src/redux/selectors.js +++ b/src/redux/selectors.js @@ -1,24 +1,46 @@ import { createSelector } from 'reselect'; const restaurantsSelector = (state) => state.restaurants.entities; -const productsSelector = (state) => state.products; +const productsSelector = (state) => state.products.entities; const orderSelector = (state) => state.order; -const reviewsSelector = (state) => state.reviews; -const usersSelector = (state) => state.users; +const reviewsSelector = (state) => state.reviews.entities; +const usersSelector = (state) => state.users.entities; +const reviewsObject = (state) => state.reviews; export const activeIdRestaurantSelector = (state) => state.restaurants.activeId; export const restaurantsLoadingSelector = (state) => state.restaurants.loading; export const restaurantsLoadedSelector = (state) => state.restaurants.loaded; - export const restaurantsListSelector = createSelector( restaurantsSelector, Object.values ); - export const restaurantSelector = (state, { id }) => restaurantsSelector(state)[id]; + export const productSelector = (state, { id }) => productsSelector(state)[id]; -export const reviewSelector = (state, { id }) => reviewsSelector(state)[id]; +export const productLoadingSelector = (state, { restId }) => { + return state.products.loading[restId]; +}; +export const productLoadedSelector = (state, { restId }) => + state.products.loaded[restId]; + +export const reviewSelector = (state, { id }) => { + return reviewsSelector(state)[id]; +}; +export const reviewLoadingSelector = (state, { restId }) => { + return state.reviews.loading[restId] || false; +}; +export const reviewLoadedSelector = (state, { restId }) => { + return state.reviews.loaded[restId] || false; +}; + +export const reviewLoadedUsers = (state) => { + return state.users.loaded; +}; +export const reviewLoadingUsers = (state) => { + return state.users.loading; +}; + export const amountSelector = (state, { id }) => orderSelector(state)[id] || 0; export const orderProductsSelector = createSelector( orderSelector, @@ -43,16 +65,22 @@ export const totalSelector = createSelector( export const reviewWitUserSelector = createSelector( reviewSelector, usersSelector, - (review, users) => ({ - ...review, - user: users[review.userId]?.name, - }) + (review, users) => { + console.log(review); + console.log(users); + return { + ...review, + user: users[review.userId]?.name, + }; + } ); export const averageRatingSelector = createSelector( reviewsSelector, restaurantSelector, - (reviews, restaurant) => { + reviewsObject, + (reviews, restaurant, review) => { + if (!review.loaded[restaurant.id]) return 3; const ratings = restaurant.reviews.map((id) => reviews[id].rating); return Math.round( ratings.reduce((acc, rating) => acc + rating) / ratings.length From d014bb84bd0d2b9ae4eabf3ffd5d2a9343448423 Mon Sep 17 00:00:00 2001 From: Alexey Date: Fri, 29 Oct 2021 09:30:57 +0300 Subject: [PATCH 2/2] HT-5 kostenevich FIXED --- src/components/restaurant/restaurant.js | 27 ++++++++++++++++++++++--- src/components/reviews/reviews.js | 3 +-- src/redux/reducer/reviews.js | 1 - src/redux/selectors.js | 2 -- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/components/restaurant/restaurant.js b/src/components/restaurant/restaurant.js index 8cb7a7f..c0374fc 100644 --- a/src/components/restaurant/restaurant.js +++ b/src/components/restaurant/restaurant.js @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import Menu from '../menu'; @@ -9,9 +9,23 @@ import Tabs from '../tabs'; import { averageRatingSelector, restaurantSelector, + reviewLoadedSelector, + reviewLoadingSelector, } from '../../redux/selectors'; +import { loadReviews } from '../../redux/actions'; + +const Restaurant = ({ + restaurant, + averageRating, + loaded, + loading, + loadReviews, + restId, +}) => { + useEffect(() => { + if (!loaded && !loading) loadReviews(restId); + }, [restId, loadReviews, loaded, loading]); -const Restaurant = ({ restaurant, averageRating }) => { const { id, name, menu, reviews } = restaurant; const [activeTab, setActiveTab] = useState('menu'); @@ -46,6 +60,13 @@ Restaurant.propTypes = { const mapStateToProps = (state, props) => ({ restaurant: restaurantSelector(state, props), averageRating: averageRatingSelector(state, props), + restId: props.id, + loaded: reviewLoadedSelector(state, props), + loading: reviewLoadingSelector(state, props), +}); + +const mapDispatchToProps = (dispatch, props) => ({ + loadReviews: () => dispatch(loadReviews(props.id)), }); -export default connect(mapStateToProps)(Restaurant); +export default connect(mapStateToProps, mapDispatchToProps)(Restaurant); diff --git a/src/components/reviews/reviews.js b/src/components/reviews/reviews.js index 8630dd7..038567f 100644 --- a/src/components/reviews/reviews.js +++ b/src/components/reviews/reviews.js @@ -38,7 +38,7 @@ const Reviews = ({ ]); if (loading || loadingUsers) return ; - if (!loaded && !loadedUsers) return 'No data :('; + if (!loaded || !loadedUsers) return 'No data :('; return (
@@ -56,7 +56,6 @@ Reviews.propTypes = { }; const mapStateToProps = (state, props) => { - console.log(props); return { restId: props.restId, loadedUsers: reviewLoadedUsers(state), diff --git a/src/redux/reducer/reviews.js b/src/redux/reducer/reviews.js index ebe5925..2778922 100644 --- a/src/redux/reducer/reviews.js +++ b/src/redux/reducer/reviews.js @@ -36,7 +36,6 @@ export default produce((draft = initialState, action) => { return draft; case LOAD_REVIEWS + FAILURE: - console.log(action); draft.loaded[restId] = false; draft.loading[restId] = false; draft.error[restId] = error; diff --git a/src/redux/selectors.js b/src/redux/selectors.js index e9b8607..b83f834 100644 --- a/src/redux/selectors.js +++ b/src/redux/selectors.js @@ -66,8 +66,6 @@ export const reviewWitUserSelector = createSelector( reviewSelector, usersSelector, (review, users) => { - console.log(review); - console.log(users); return { ...review, user: users[review.userId]?.name,