@@ -38,4 +68,11 @@ class Menu extends React.Component {
}
}
-export default Menu;
+export default connect(
+ (state) => ({
+ menu: productsListSelector(state),
+ loading: productsLoadingSelector(state),
+ loaded: productsLoadedSelector(state),
+ }),
+ { loadProducts }
+)(Menu);
diff --git a/src/components/restaurant/restaurant.js b/src/components/restaurant/restaurant.js
index 7f4ec3e..40797ac 100644
--- a/src/components/restaurant/restaurant.js
+++ b/src/components/restaurant/restaurant.js
@@ -11,7 +11,7 @@ import { averageRatingSelector } from '../../redux/selectors';
const Restaurant = ({ restaurant, averageRating }) => {
const { id, name, menu, reviews } = restaurant;
const tabs = [
- { title: 'Menu', content:
},
+ { title: 'Menu', content:
},
{
title: 'Reviews',
content:
,
@@ -20,9 +20,7 @@ const Restaurant = ({ restaurant, averageRating }) => {
return (
-
-
-
+ {/* */}
);
@@ -39,5 +37,5 @@ Restaurant.propTypes = {
};
export default connect((state, props) => ({
- averageRating: averageRatingSelector(state, props),
+ // averageRating: averageRatingSelector(state, props),
}))(Restaurant);
diff --git a/src/components/reviews/reviews.js b/src/components/reviews/reviews.js
index b224553..967c404 100644
--- a/src/components/reviews/reviews.js
+++ b/src/components/reviews/reviews.js
@@ -5,17 +5,38 @@ 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 {
+ usersListSelector,
+ usersLoadingSelector,
+ usersLoadedSelector,
+ reviewsListSelector,
+ reviewsLoadingSelector,
+ reviewsLoadedSelector,
+} from '../../redux/selectors';
+import Loader from '../loader';
-const Reviews = ({ reviews, restaurantId, loadReviews }) => {
+const Reviews = ({
+ reviews,
+ restaurantId,
+ loadReviews,
+ loadUsers,
+ loading,
+ loaded,
+}) => {
useEffect(() => {
- loadReviews(restaurantId);
- }, [loadReviews, restaurantId]);
+ loadUsers();
+ if (!loading && !loaded) {
+ loadReviews(restaurantId);
+ }
+ }, [loading, loaded, loadUsers, loadReviews, restaurantId]);
+
+ if (loading || !loaded) return
;
return (
- {reviews.map((id) => (
-
+ {reviews.map((review) => (
+
))}
@@ -24,7 +45,19 @@ const Reviews = ({ reviews, restaurantId, loadReviews }) => {
Reviews.propTypes = {
restaurantId: PropTypes.string.isRequired,
- reviews: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
+ reviews: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired,
};
-export default connect(null, { loadReviews })(Reviews);
+export default connect(
+ ((state) => ({
+ users: usersListSelector(state),
+ loading: usersLoadingSelector(state),
+ loaded: usersLoadedSelector(state),
+ }),
+ (state) => ({
+ reviews: reviewsListSelector(state),
+ loading: reviewsLoadingSelector(state),
+ loaded: reviewsLoadedSelector(state),
+ })),
+ { loadReviews, loadUsers }
+)(Reviews);
diff --git a/src/fixtures.js b/src/fixtures.js
deleted file mode 100644
index 3c84f3b..0000000
--- a/src/fixtures.js
+++ /dev/null
@@ -1,390 +0,0 @@
-const restaurants = [
- {
- id: 'a757a0e9-03c1-4a2a-b384-8ac21dbe2fb2',
- name: 'Dishoom',
- location: {
- lat: 51.51307933813641,
- lng: -0.13968944549560547,
- },
- image:
- 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/12065623_177150569292352_3493242754411252126_n.jpg?_nc_cat=108&_nc_ht=scontent-waw1-1.xx&oh=94f9c77559a2f31edf4fd68a89b6dda7&oe=5D6D6D4A',
- 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',
- location: {
- lat: 51.51847684708113,
- lng: -0.13999606534701844,
- },
- image:
- 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/14492346_873920782710134_3797018371088115698_n.jpg?_nc_cat=108&_nc_ht=scontent-waw1-1.xx&oh=31de8f2906284981f858f7fdf36b0235&oe=5D6AA774',
- 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',
- location: {
- lat: 51.513614456342495,
- lng: -0.1284961359927072,
- },
- image: 'http://fabrique.co.uk/wp-content/uploads/2012/11/our22.png',
- 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',
- location: {
- lat: 51.51084146746025,
- lng: -0.12409270211070839,
- },
- image:
- 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/602319_106412009502974_1112399097_n.jpg?_nc_cat=110&_nc_ht=scontent-waw1-1.xx&oh=c23931271965e9edd3921d559f7440ba&oe=5D6CC921',
- 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,
- },
- ],
- },
-];
-
-const normalizedRestaurants = [
- {
- id: 'a757a0e9-03c1-4a2a-b384-8ac21dbe2fb2',
- name: 'Dishoom',
- location: {
- lat: 51.51307933813641,
- lng: -0.13968944549560547,
- },
- image:
- 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/12065623_177150569292352_3493242754411252126_n.jpg?_nc_cat=108&_nc_ht=scontent-waw1-1.xx&oh=94f9c77559a2f31edf4fd68a89b6dda7&oe=5D6D6D4A',
- 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',
- location: {
- lat: 51.51847684708113,
- lng: -0.13999606534701844,
- },
- image:
- 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/14492346_873920782710134_3797018371088115698_n.jpg?_nc_cat=108&_nc_ht=scontent-waw1-1.xx&oh=31de8f2906284981f858f7fdf36b0235&oe=5D6AA774',
- 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',
- location: {
- lat: 51.513614456342495,
- lng: -0.1284961359927072,
- },
- image: 'http://fabrique.co.uk/wp-content/uploads/2012/11/our22.png',
- 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',
- location: {
- lat: 51.51084146746025,
- lng: -0.12409270211070839,
- },
- image:
- 'https://scontent-waw1-1.xx.fbcdn.net/v/t1.0-9/602319_106412009502974_1112399097_n.jpg?_nc_cat=110&_nc_ht=scontent-waw1-1.xx&oh=c23931271965e9edd3921d559f7440ba&oe=5D6CC921',
- menu: [
- '6c02c2ce-b868-4191-b4a7-8686429f4bac',
- '99bb6fbb-e53b-4b7e-b9c2-23b63b77385d',
- ],
- reviews: [
- '5db6247b-ab1c-49db-be1f-8dd27fd38b81',
- '381b0c31-6360-43ff-80d1-581a116159d8',
- ],
- },
-];
-
-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'],
- },
-];
-
-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,
- },
-];
-
-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',
- },
-];
-
-export {
- restaurants,
- normalizedProducts,
- normalizedRestaurants,
- normalizedReviews,
- normalizedUsers,
-};
diff --git a/src/redux/actions.js b/src/redux/actions.js
index 42b28b2..ccfbabb 100644
--- a/src/redux/actions.js
+++ b/src/redux/actions.js
@@ -4,7 +4,9 @@ import {
REMOVE,
ADD_REVIEW,
LOAD_RESTAURANTS,
+ LOAD_PRODUCTS,
LOAD_REVIEWS,
+ LOAD_USERS,
REQUEST,
SUCCESS,
FAILURE,
@@ -25,9 +27,13 @@ export const loadRestaurants = () => ({
CallAPI: '/api/restaurants',
});
+export const loadProducts = (restaurantId) => ({
+ type: LOAD_PRODUCTS,
+ CallAPI: `/api/products?id=${restaurantId}`,
+});
+
export const loadReviews = (restaurantId) => async (dispatch) => {
dispatch({ type: LOAD_REVIEWS + REQUEST });
-
try {
const data = await fetch(`/api/reviews?id=${restaurantId}`).then((res) =>
res.json()
@@ -37,3 +43,13 @@ export const loadReviews = (restaurantId) => async (dispatch) => {
dispatch({ type: LOAD_REVIEWS + FAILURE, error });
}
};
+
+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 a47d519..27ba575 100644
--- a/src/redux/constants.js
+++ b/src/redux/constants.js
@@ -4,7 +4,9 @@ export const REMOVE = 'REMOVE';
export const ADD_REVIEW = 'ADD_REVIEW';
export const LOAD_RESTAURANTS = 'LOAD_RESTAURANTS';
+export const LOAD_PRODUCTS = 'LOAD_PRODUCTS';
export const LOAD_REVIEWS = 'LOAD_REVIEWS';
+export const LOAD_USERS = 'LOAD_USERS';
export const REQUEST = '_REQUEST';
export const SUCCESS = '_SUCCESS';
diff --git a/src/redux/reducer/products.js b/src/redux/reducer/products.js
index c87287b..efd2ecf 100644
--- a/src/redux/reducer/products.js
+++ b/src/redux/reducer/products.js
@@ -1,11 +1,38 @@
-import { normalizedProducts } from '../../fixtures';
+import { LOAD_PRODUCTS, SUCCESS, FAILURE, REQUEST } from '../constants';
+
import { arrToMap } from '../utils';
-// { [productId]: product }
-export default (state = arrToMap(normalizedProducts), action) => {
- const { type } = action;
+const initialState = {
+ menu: {},
+ loading: false,
+ loaded: false,
+ error: null,
+};
+
+export default (state = initialState, action) => {
+ const { type, data, error } = action;
switch (type) {
+ case LOAD_PRODUCTS + REQUEST:
+ return {
+ ...state,
+ loading: true,
+ error: null,
+ };
+ case LOAD_PRODUCTS + SUCCESS:
+ return {
+ ...state,
+ menu: arrToMap(data),
+ loading: false,
+ loaded: true,
+ };
+ case LOAD_PRODUCTS + FAILURE:
+ return {
+ ...state,
+ loading: false,
+ loaded: false,
+ error,
+ };
default:
return state;
}
diff --git a/src/redux/reducer/reviews.js b/src/redux/reducer/reviews.js
index 787f625..a0dbd84 100644
--- a/src/redux/reducer/reviews.js
+++ b/src/redux/reducer/reviews.js
@@ -1,17 +1,56 @@
-import { ADD_REVIEW } from '../constants';
-import { normalizedReviews } from '../../fixtures';
+import produce from 'immer';
+import {
+ ADD_REVIEW,
+ REQUEST,
+ SUCCESS,
+ FAILURE,
+ LOAD_REVIEWS,
+} from '../constants';
import { arrToMap } from '../utils';
-export default (state = arrToMap(normalizedReviews), action) => {
- const { type, payload, reviewId, userId } = action;
+const initialState = {
+ entities: {},
+ loading: false,
+ loaded: false,
+ error: null,
+};
+
+export default (state = initialState, action) => {
+ const { type, payload, reviewId, userId, data, error } = action;
switch (type) {
- case ADD_REVIEW:
- const { text, rating } = payload.review;
+ case LOAD_REVIEWS + REQUEST:
+ return {
+ ...state,
+ loading: true,
+ error: null,
+ };
+ case LOAD_REVIEWS + SUCCESS:
return {
...state,
- [reviewId]: { id: reviewId, userId, text, rating },
+ entities: arrToMap(data),
+ loading: false,
+ loaded: true,
};
+ case LOAD_REVIEWS + FAILURE:
+ return {
+ ...state,
+ loading: false,
+ loaded: false,
+ error,
+ };
+
+ case ADD_REVIEW:
+ const { text, rating } = payload.review;
+ return produce(state, (draft) => {
+ draft.entities[reviewId] = {
+ id: reviewId,
+ userId,
+ text,
+ rating,
+ };
+ });
+
default:
return state;
}
diff --git a/src/redux/reducer/users.js b/src/redux/reducer/users.js
index 063b7be..8c3cfe0 100644
--- a/src/redux/reducer/users.js
+++ b/src/redux/reducer/users.js
@@ -1,12 +1,45 @@
import produce from 'immer';
-import { ADD_REVIEW } from '../constants';
-import { normalizedUsers } from '../../fixtures';
+import {
+ ADD_REVIEW,
+ LOAD_USERS,
+ REQUEST,
+ SUCCESS,
+ FAILURE,
+} from '../constants';
import { arrToMap } from '../utils';
-export default produce((draft = arrToMap(normalizedUsers), action) => {
- const { type, payload, userId } = action;
+const initialState = {
+ entities: {},
+ loading: false,
+ loaded: false,
+ error: null,
+};
+
+export default produce((draft = initialState, action) => {
+ const { type, payload, userId, data, error } = action;
switch (type) {
+ case LOAD_USERS + REQUEST:
+ return {
+ ...draft,
+ loading: true,
+ error: null,
+ };
+ case LOAD_USERS + SUCCESS:
+ return {
+ ...draft,
+ entities: arrToMap(data),
+ loading: false,
+ loaded: true,
+ };
+ case LOAD_USERS + FAILURE:
+ return {
+ ...draft,
+ loading: false,
+ loaded: false,
+ error,
+ };
+
case ADD_REVIEW:
const { name } = payload.review;
draft[userId] = { id: userId, name };
diff --git a/src/redux/selectors.js b/src/redux/selectors.js
index fbbe656..37ea11a 100644
--- a/src/redux/selectors.js
+++ b/src/redux/selectors.js
@@ -3,13 +3,30 @@ import { getById } from './utils';
const restaurantsSelector = (state) => state.restaurants.entities;
const orderSelector = (state) => state.order;
-const productsSelector = (state) => state.products;
-const reviewsSelector = (state) => state.reviews;
-const usersSelector = (state) => state.users;
+const productsSelector = (state) => state.products.menu;
+const reviewsSelector = (state) => state.reviews.entities;
+const usersSelector = (state) => state.users.entities;
-export const restaurantsLoadingSelector = (state) => state.restaurants.loading;
-export const restaurantsLoadedSelector = (state) => state.restaurants.loaded;
+export const productsLoadingSelector = (state) => state.products.loading;
+export const productsLoadedSelector = (state) => state.products.loaded;
+export const productsListSelector = createSelector(
+ productsSelector,
+ Object.values
+);
+
+export const usersLoadingSelector = (state) => state.users.loading;
+export const usersLoadedSelector = (state) => state.users.loaded;
+export const usersListSelector = createSelector(usersSelector, Object.values);
+export const reviewsLoadingSelector = (state) => state.reviews.loading;
+export const reviewsLoadedSelector = (state) => state.reviews.loaded;
+export const reviewsListSelector = createSelector(
+ reviewsSelector,
+ Object.values
+);
+
+export const restaurantsLoadedSelector = (state) => state.restaurants.loaded;
+export const restaurantsLoadingSelector = (state) => state.restaurants.loading;
export const restaurantsListSelector = createSelector(
restaurantsSelector,
Object.values