Загрузка изображения на сервер

  1. Делаем проверку на то, является пребывает ли пользователь в данный момент на своей странице профиля (хозяин он ее или не хозин). Если у нас 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} />
                )
        }
    }
    
    ....

     

  2. Теперь в 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;

     

  3. В компоненте 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;

     

  4. Теперь вешаем на этот импут через обработчик событий 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} />
    ....
  5. Теперь нам в 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)
  6. В “промежуточной” компоненте 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;
  7. Теперь идем туда, где у нас санкриэйтеры (условно говоря, санки) – src\redux\profile-reducer.js. Там в конце последней санкой добавляем нашу новую savePhoto. В ней, забегая немного наперед, видно диспатч обычного экшнкриэейтера savePhotoSuccess, который будет диспатчиться, если санка выполниться успешно и передавать новое фото, пришедшее в обновленном объекте photos. В этом сидящем в юзере объекте у нас приходит фото большое (large) и маленькое (large).
    Содержимое объекта photo
  8. Создадим выше в этом же файле сам экшн криэейтер 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, который был в предыдущем примере кода

     

  9. Осталось нам только добавить функцию 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'
                  }
            });
        }
    }

     

Один ответ к «Загрузка изображения на сервер»

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *