- Делаем проверку на то, является пребывает ли пользователь в данный момент на своей странице профиля (хозяин он ее или не хозин). Если у нас userId сейчас underfined, то мы находимся на профайле без userId, т.е. в режиме как бы owner – владельца страницы.
В итоге мы в файле src\components\Profile\ProfileContainer.jsx делаем эту проверку и прокидываем в пропсы дальше в компоненту Profile:
.....
render() {
return (
<Profile {...this.props}
isOwner={!this.props.match.params.userId} //вот здесь происходит эта проверка и передача дальше
profile={this.props.profile}
status={this.props.status}
updateStatus={this.props.updateStatus}
savePhoto={this.props.savePhoto} />
)
}
}
....
- Теперь в Profile прокидываем этот пропс еще глубже – в ProfileInfo.jsx:
import ...
const Profile = (props) => {
return (
<div>
<ProfileInfo isOwner={props.isOwner} //вот здесь
profile={props.profile}
status={props.status}
updateStatus={props.updateStatus} />
<MyPostsContainer />
</div>
)
}
export default Profile;
- В компоненте ProfileInfo если наш isOwner равно true, то показываем input с типом файл:
import...
const ProfileInfo = ({profile, status, updateStatus, isOwner, savePhoto}) => {
if (!profile) {
return <Preloader />
}
const mainPhotoSelected = (e) => {
if (e.target.files.length) {
savePhoto(e.target.files[0])
}
}
return (
<div>
<div className={s.bigAvatar}>
<img src="https://atlantis.nyc3.digitaloceanspaces.com/styled/72025f140f22a3eb32950bbb9d76e68d" />
</div>
<div className={s.descriptionBlock}>
<img src={profile.photos.large || userPhoto} className={s.main} />
{isOwner && <input type={"file"} onChange={mainPhotoSelected} />} //вот здесь
<ProfileStatusWithHooks status={status} updateStatus={updateStatus} />
</div>
</div>
)
}
export default ProfileInfo;
- Теперь вешаем на этот импут через обработчик событий onChange функцию mainPhotoSelected и передаем в нее санку savePhoto (точнее санкриэйтер), которая будет приходить из пропсов:
import...
const ProfileInfo = ({profile, status, updateStatus, isOwner, savePhoto}) => {
if (!profile) {
return <Preloader />
}
const mainPhotoSelected = (e) => {
if (e.target.files.length) {
savePhoto(e.target.files[0])
}
}
return (
<div>
<div className={s.bigAvatar}>
<img src="https://atlantis.nyc3.digitaloceanspaces.com/styled/72025f140f22a3eb32950bbb9d76e68d" />
</div>
<div className={s.descriptionBlock}>
<img src={profile.photos.large || userPhoto} className={s.main} />
{isOwner && <input type={"file"} onChange={mainPhotoSelected} />} //повесили событие на инпут
<ProfileStatusWithHooks status={status} updateStatus={updateStatus} />
....
- Теперь нам в ProfileContainer нужно аналогично другой санке (например, updateStatus, которая была последней) организовать «обработку» savePhoto. Т.е. импортировать ее из profile-reducer, законектить к компоненте ProfileContainer и передать дальше через пропсы:
import React, { Component } from 'react';
import Profile from './Profile';
import { connect } from 'react-redux';
import {getUserProfile, getStatus, updateStatus, savePhoto} from '../../redux/profile-reducer';
import {withRouter} from 'react-router-dom';
import { compose } from 'redux';
class ProfileContainer extends React.Component {
refrehsProfile() { //вспомогательный метод, чтобы код не дублировался в маунт и апдейт одинаковый
...
}
componentDidMount() {
this.refrehsProfile();
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (this.props.match.params.userId != prevProps.match.params.userId) {
this.refrehsProfile();
}
}
render() {
return (
<Profile {...this.props}
isOwner={!this.props.match.params.userId}
profile={this.props.profile}
status={this.props.status}
updateStatus={this.props.updateStatus}
savePhoto={this.props.savePhoto} />
)
}
}
let mapStateToProps = (state) => ({
profile: state.profilePage.profile,
status: state.profilePage.status,
authorizedUserId: state.auth.userId,
isAuth: state.auth.isAuth
});
export default compose(
connect(mapStateToProps, {getUserProfile, getStatus, updateStatus, savePhoto}),
withRouter,
//withAuthRedirect
)(ProfileContainer)
- В «промежуточной» компоненте Profile тоже прокидываем через пропсы, чтобы наша санка дошла таки до ProfileInfo, где мы ее выше уже вписали в функкцию, подвешенную на обработчик событий.
import ...
const Profile = (props) => {
return (
<div>
<ProfileInfo savePhoto={props.savePhoto} //вот
isOwner={props.isOwner}
profile={props.profile}
status={props.status}
updateStatus={props.updateStatus} />
<MyPostsContainer />
</div>
)
}
export default Profile;
- Теперь идем туда, где у нас санкриэйтеры (условно говоря, санки) – src\redux\profile-reducer.js. Там в конце последней санкой добавляем нашу новую savePhoto. В ней, забегая немного наперед, видно диспатч обычного экшнкриэейтера savePhotoSuccess, который будет диспатчиться, если санка выполниться успешно и передавать новое фото, пришедшее в обновленном объекте photos. В этом сидящем в юзере объекте у нас приходит фото большое (large) и маленькое (large).
- Создадим выше в этом же файле сам экшн криэейтер savePhotoSuccess, который и диспатчили в санку. А еще чуть выше редьюсер, в котором обрабатываем этот тип экшна и меняем фото на то, которое пришло в экшине:
import { usersAPI, profileAPI } from "../api/api";
...
const SAVE_PHOTO_SUCCESS = 'SAVE_PHOTO_SUCCESS';
let initialeState = {
posts: [
{id: 1, message: 'Hi, how are you', likesCount:10},
{id: 2, message: 'Its is my first post', likesCount:15},
...
],
profile: null,
status: ""
};
export const profileReducer = (state = initialeState, action) => {
switch(action.type) {
...
}
case SAVE_PHOTO_SUCCESS: { //вот наш редьюсер
return {...state, profile: {...state.profile, photos: action.photos}};
}
default:
return state;
}
}
//дальше пошли санкриэйтеры, в т.ч. наш savePhoto, который был в предыдущем примере кода
- Осталось нам только добавить функцию savePhoto в файл src\api\api.js, которая вызывается в нашем санккриэйтере savePhoto и загружает фото на сервер. Обратите внимание на передаваемый заголовок
'Content-Type': 'multipart/form-data'
, так как это не какой-то там json, а файл. Но этот заголовок можно было и не указывать, так как он отправляется по умолчанию.
export const profileAPI = {
...,
savePhoto(photoFile) {
const formData = new FormData();
formData.append("image", photoFile)
return instance.put('profile/photo', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
}
}
body{ background-color: red }