-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HT4 #41
base: master
Are you sure you want to change the base?
HT4 #41
Changes from all commits
dbbd607
42211a2
5fd1eb4
5c9f65c
e2ac294
158fe86
0cf169f
a121213
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,31 @@ | ||
import React, { useMemo } from 'react'; | ||
import { connect } from 'react-redux'; | ||
import PropTypes from 'prop-types'; | ||
import Menu from '../menu'; | ||
import Reviews from '../reviews'; | ||
import Banner from '../banner'; | ||
import Rate from '../rate'; | ||
import Tabs from '../tabs'; | ||
import { reviewsSelector, restaurantsSelector } from '../../redux/selectors'; | ||
|
||
const Restaurant = ({ restaurant }) => { | ||
const { name, menu, reviews } = restaurant; | ||
const Restaurant = ({ restaurant, restaurantReviews }) => { | ||
const { name, menu, reviews, id } = restaurant; | ||
|
||
const averageRating = useMemo(() => { | ||
const total = reviews.reduce((acc, { rating }) => acc + rating, 0); | ||
const total = reviews.reduce( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. это лучше перенести в селекторы |
||
(acc, id) => acc + restaurantReviews[id].rating, | ||
0 | ||
); | ||
|
||
return Math.round(total / reviews.length); | ||
}, [reviews]); | ||
}, [reviews, restaurantReviews]); | ||
|
||
const tabs = [ | ||
{ title: 'Menu', content: <Menu menu={menu} /> }, | ||
{ title: 'Reviews', content: <Reviews reviews={reviews} /> }, | ||
{ | ||
title: 'Reviews', | ||
content: <Reviews reviews={reviews} activeRestaurantId={id} />, | ||
}, | ||
]; | ||
|
||
return ( | ||
|
@@ -32,13 +41,14 @@ const Restaurant = ({ restaurant }) => { | |
Restaurant.propTypes = { | ||
restaurant: PropTypes.shape({ | ||
name: PropTypes.string, | ||
menu: PropTypes.array, | ||
reviews: PropTypes.arrayOf( | ||
PropTypes.shape({ | ||
rating: PropTypes.number.isRequired, | ||
}).isRequired | ||
).isRequired, | ||
menu: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, | ||
reviews: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, | ||
}).isRequired, | ||
}; | ||
|
||
export default Restaurant; | ||
const mapStateToProps = (state, ownProps) => ({ | ||
restaurant: restaurantsSelector(state)[ownProps.id], | ||
restaurantReviews: reviewsSelector(state), | ||
}); | ||
|
||
export default connect(mapStateToProps)(Restaurant); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,24 +3,25 @@ import { connect } from 'react-redux'; | |
import PropTypes from 'prop-types'; | ||
import Restaurant from '../restaurant'; | ||
import Tabs from '../tabs'; | ||
import { restaurantsSelector } from '../../redux/selectors'; | ||
|
||
const Restaurants = ({ restaurants }) => { | ||
const tabs = restaurants.map((restaurant) => ({ | ||
const tabs = Object.values(restaurants).map((restaurant) => ({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Object. values(restaurants) должно быть в селекторах и мемоизировано |
||
title: restaurant.name, | ||
content: <Restaurant restaurant={restaurant} />, | ||
content: <Restaurant id={restaurant.id} />, | ||
})); | ||
|
||
return <Tabs tabs={tabs} />; | ||
}; | ||
|
||
Restaurants.propTypes = { | ||
restaurants: PropTypes.arrayOf( | ||
restaurants: PropTypes.shape( | ||
PropTypes.shape({ | ||
id: PropTypes.string.isRequired, | ||
}).isRequired | ||
).isRequired, | ||
}; | ||
|
||
export default connect((state) => ({ | ||
restaurants: state.restaurants, | ||
restaurants: restaurantsSelector(state), | ||
}))(Restaurants); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,56 @@ | ||
import React from 'react'; | ||
import { connect } from 'react-redux'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import Rate from '../../rate'; | ||
import styles from './review.module.css'; | ||
|
||
const Review = ({ user, text, rating }) => ( | ||
import { reviewsSelector, usersSelector } from '../../../redux/selectors'; | ||
|
||
const Review = ({ review, userName }) => ( | ||
<div className={styles.review} data-id="review"> | ||
<div className={styles.content}> | ||
<div> | ||
<h4 className={styles.name} data-id="review-user"> | ||
{user} | ||
{userName.name} | ||
</h4> | ||
<p className={styles.comment} data-id="review-text"> | ||
{text} | ||
{review.text} | ||
</p> | ||
</div> | ||
<div className={styles.rate}> | ||
<Rate value={rating} /> | ||
<Rate value={review.rating} /> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
|
||
Review.propTypes = { | ||
user: PropTypes.string, | ||
text: PropTypes.string, | ||
rating: PropTypes.number.isRequired, | ||
review: PropTypes.shape({ | ||
id: PropTypes.string.isRequired, | ||
userId: PropTypes.string, | ||
text: PropTypes.string, | ||
rating: PropTypes.number.isRequired, | ||
}).isRequired, | ||
userName: PropTypes.shape({ | ||
id: PropTypes.string.isRequired, | ||
name: PropTypes.string, | ||
}).isRequired, | ||
}; | ||
|
||
Review.defaultProps = { | ||
user: 'Anonymous', | ||
userName: { name: 'Anonymous' }, | ||
}; | ||
|
||
const mapStateToProps = (state, ownProps) => { | ||
const review = reviewsSelector(state)[ownProps.id]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. все эти вычисления нужно выносить в селекторы, чтобы компонент занимался только рендером |
||
const userId = review.userId; | ||
const userName = usersSelector(state)[userId]; | ||
|
||
return { | ||
review, | ||
userName, | ||
}; | ||
}; | ||
|
||
export default Review; | ||
export default connect(mapStateToProps, null)(Review); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
import { INCREMENT, DECREMENT, REMOVE } from './constants'; | ||
import { INCREMENT, DECREMENT, REMOVE, ADD_REVIEW } from './constants'; | ||
|
||
export const increment = (id) => ({ type: INCREMENT, payload: { id } }); | ||
export const decrement = (id) => ({ type: DECREMENT, payload: { id } }); | ||
export const remove = (id) => ({ type: REMOVE, payload: { id } }); | ||
export const addReview = (values, activeRestaurantId) => ({ | ||
type: ADD_REVIEW, | ||
payload: { values, activeRestaurantId }, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export const INCREMENT = 'INCREMENT'; | ||
export const DECREMENT = 'DECREMENT'; | ||
export const REMOVE = 'REMOVE'; | ||
export const ADD_REVIEW = 'ADD_REVIEW'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { v4 as uuidv4 } from 'uuid'; | ||
|
||
export default (state) => (next) => (action) => { | ||
if (action.type === 'ADD_REVIEW') { | ||
const newReviewId = uuidv4(); | ||
const newUserId = uuidv4(); | ||
|
||
action.payload.values = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. не стоит мутировать action, лучше создать новый |
||
id: newReviewId, | ||
userId: newUserId, | ||
...action.payload.values, | ||
}; | ||
} | ||
|
||
next(action); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,31 @@ | ||
import { normalizedRestaurants as defaultRestaurants } from '../../fixtures'; | ||
import { normalizedRestaurants } from '../../fixtures'; | ||
import { ADD_REVIEW } from '../constants'; | ||
|
||
const defaultRestaurants = normalizedRestaurants.reduce( | ||
(acc, restaurant) => ({ | ||
...acc, | ||
[restaurant.id]: restaurant, | ||
}), | ||
{} | ||
); | ||
|
||
export default (restaurants = defaultRestaurants, action) => { | ||
const { type } = action; | ||
const { type, payload } = action; | ||
|
||
switch (type) { | ||
case ADD_REVIEW: { | ||
const addedNewReview = payload.values.id; | ||
const activeRestaurantId = payload.activeRestaurantId.activeRestaurantId; | ||
const activeRestaurantData = restaurants[activeRestaurantId]; | ||
|
||
return { | ||
...restaurants, | ||
[activeRestaurantId]: { | ||
...activeRestaurantData, | ||
...activeRestaurantData.reviews.push(addedNewReview), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. это все равно мутация reviews по ссылке, такого делать нельзя |
||
}, | ||
}; | ||
} | ||
default: | ||
return restaurants; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { normalizedUsers } from '../../fixtures'; | ||
import { ADD_REVIEW } from '../constants'; | ||
|
||
const defaultUsers = normalizedUsers.reduce( | ||
(acc, user) => ({ ...acc, [user.id]: user }), | ||
{} | ||
); | ||
|
||
export default (users = defaultUsers, action) => { | ||
const { type, payload } = action; | ||
|
||
switch (type) { | ||
case ADD_REVIEW: { | ||
const newUserId = payload.values.userId; | ||
const newUserName = payload.values.name; | ||
|
||
return { | ||
...users, | ||
[newUserId]: { | ||
id: newUserId, | ||
name: newUserName, | ||
}, | ||
}; | ||
} | ||
default: | ||
return users; | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
лучше сразу делать селекторы, которые принимаю и стейт и пропы - orderSelector(state, ownProps) чтобы менять потом в компоненте при изменении структуры данных