- Создаем файл для редьюсера –
src/redux/auth-reducer.js
- Создаем стейт с теми же свойствами, которые мы будем получать, согласно API-документации. При этом “зануляем” их.
-
let initialState = {
userId: null,
email: null,
login: null,
isAuth: false
}
isAuth – это не из пришедшего объекта, а для удобства нам на будушее.
- Создадим переменную с типом экшна, приходящего когда мы авторизированы (в самом верху файла):
const SET_USER_DATA = 'SET_USER_DATA';
- Создаем редьюсер authReducer. В нем мы создаем копию state, а следующей строчкой делаем деструктуризацию объекта data, который пришел в экшене. В резульате userId, email, login перезатрут аналогичные значения в копии state, так как эта строчка кода ниже. А также меняем потом в копии state значение свойства isAuth на false.
const authReducer = (state = initialState, action) => {
switch(action.type) {
case SET_USER_DATA:
return {
...state,
...action.data,
isAuth: true
}
default:
return state;
}
}
export default authReducer;
- В самом низу файла перед экспортом редьюсера создаем экшн-криэйтер, который будет называться setUserData. В него приходят данные userId, email, login (это мы их передаем в разобраном виде, хотя можно было и объектом). Экшн-криэйтер будет возвращать экшн, который содержит type: SET_USER_DATA, а также объект с данными data: {userId, email, login}. Можна было бы передать одним объеком data, но тогда человеку, который будет вызывать экшн-криэйтер будет не до конца понятно, что за data и какого она типа должна быть (такую проблему возможно решить языком со строгой типизацией, но это в будущем). Этот созданный экшн потом будет задиспатчен в редьюсер.
export const setUserData = (userId, email, login) => ({type: SET_USER_DATA, data: {user, email, login} })
- В файле
src/redux/redux-store.js
, где мы создавали точку входа редьюсеров (т.е. объединяли их при помощи combineReducers) делаем импорт нашего нового редьюсера и добавляем его в combineReducers.
...
import authReducer from "./auth-redux";
....
let reducers = combineReducers({
...
auth: authReducer,
...
});
let store = createStore(reducers, applyMiddleware(thunkMiddleware));
export default store;
- Идем в нашу презентационную компоненту Header.jsx и там создаем новый блок div, а в нем Navlink на страницу /login
- Создаем контейнерную компоненту для Header – HeaderContainer, которая будет классовой. Заменяем в App.js вывод на нашу контейнерную компоненту вместо презентационной. Теперь, благодаря контейнерной компоненте, у нас есть возможность делать запросы, не загрязняя презентационную компоненту. Согласно документации делаем GET запрос на auth/me. Вторым параметром в GET запросе нам нужно передать специальный объект, в котором сидят настройки запроса {withCredentials: true}. Благодаря этому запрос уйдет авторизованым на сервер.
import....
class HeaderContainer extends React.Component {
componentDidMount() {
axios.get('https://social-network.samuraijs.com/api/1.0/auth/me', {
withCredentials: true
})
}
render() {
return <Header {...this.props} />
}
}
export default HeaderContainer;
- Теперь наш Header узнал, что мы авторизованы и эту информацию можно задиспатчить в редьюсеры. Для этого мы конннектим нашу контейнерную компоненту.
const mapeStateToProps = (state) => ({});
export default connect(mapeStateToProps, {getAuthUserData}) (HeaderContainer);
- Теперь в компоненте добавляем, что если мы авторизированы (response.data.resultCode === 0), то деструктуризируем наш объект data (правильную последовательность id, email и login можно в редьюсере подсмотреть) и диспатчим авторизационные данные:
import....
class HeaderContainer extends React.Component {
componentDidMount() {
axios.get('https://social-network.samuraijs.com/api/1.0/auth/me', {
withCredentials: true
})
.then(response => {
if (response.data.resultCode === 0) {
let (id, login, email) = response.data.data;
this.props.setAuthUserData(id, email, login);
}
}
render() {
return <Header {...this.props} />
}
}
...
Первая data – это стандартная аксиовская структура, а вторая – это бэкендшик так назвал объект. Поэтому и получилось две data подряд.
- Теперь, если мы залогинены и хоим показать это на странице (например, выводить логин пользователя вместо ссылки на страницу /login в header), то добавляем в mapStateToProps данные из стейта, которые мы получим в HeaderContainer и через пропсы прокиним в Header.
const mapeStateToProps = (state) => ({
isAuth: state.auth.isAuth,
login: state.auth.login
});
- В Header теперь можно через тернарный оператор вывести тот или иной результат, зависимо от авторизации. Вот так будет выглядеть весь Header.jsx:
import React from 'react';
import s from './Header.module.css';
import { NavLink } from 'react-router-dom/cjs/react-router-dom.min';
const Header = (props) => {
return (
<header className={s.header}>
<img src='https://w0.pngwave.com/png/935/389/university-of-amikom-yogyakarta-condongcatur-logo-social-media-social-media-png-clip-art.png' />
<div className={s.loginBlock}>
{props.isAuth ? props.login
: <NavLink to={'/login'}>Login</NavLink>
}
</div>
</header>
)
}
export default Header;