Captcha
Двигаться будем сверху вниз, т.е. DAL – BLL – UI.
- Создадим в «апишке» (src\api\api.js) объект для отправки запроса с целью получения url капчи:
... export const securityAPI = { getCaptchaUrl() { return instance.get(`security/get-captcha-url`); }, }
2. Теперь добавим санку внизу файла src\redux\auth-reducer.js. Учитываем, что к нам по запросу возвращается объект, у которого есть свойство url (согласно API документации).
export const getCaptchaUrl = () => async (dispatch) => { const response = await securityAPI.getCaptchaUrl(); const captchaUrl = response.data.url; }
В итоге мы получили url, который должен как-то отобарзиться в UI. А он может попасть туда только если станет стейтом.
- Поэтому добавляем в том же файле в стейт свойство captchaUrl со значением null.
let initialState = { userId: null, email: null, login: null, isAuth: false, captchaUrl: null //if null, then captcha is not requred }
- Полученный в санке url теперь нужно засестать в стейт вместо null. Для этого:
1) ложим строку в константу ET_CAPTCHA_URL_SUCCESS;
2) создадим генератор экшена (экшнкриэйтер) getCaptchaUrlSuccess;
3) добавляем его обработку внутрь функции-редьюсера authReducer,
4) диспатчим этот же экшнкриэйтер внутри нашего созданного ранее санккриэйтера getCaptchaUrl;
5) диспатчим наш санккриэйтер getCaptchaUrl для получения капчи внутри другого санккриэйтера login* для логанизации внутри условия, когда авторизация не прошла ():import { authAPI, securityAPI } from "../api/api"; import { stopSubmit } from "redux-form"; ... const GET_CAPTCHA_URL_SUCCESS = 'social/auth/GET_CAPTCHA_URL_SUCCESS'; let initialState = {...} const authReducer = (state = initialState, action) => { switch(action.type) { //обработка обоих экшенов аналогична, поэтому код короче case SET_USER_DATA: case GET_CAPTCHA_URL_SUCCESS: return { ...state, ...action.payload, } default: return state; } } export const setAuthUserData = (userId, email, login, isAuth) => ({...}); export const getCaptchaUrlSuccess = (captchaUrl) => ({ //экшнкриэйтер type: GET_CAPTCHA_URL_SUCCESS, payload: {captchaUrl} }); export const login = (email, password, rememberMe, captcha) => async (dispatch) => { let response = await authAPI.login(email, password, rememberMe, captcha) if (response.data.resultCode === 0) { dispatch(getAuthUserData()); } else { //если логинизация не прошла, то получаем капчу либо выводим ошибку if (response.data.resultCode === 10) { dispatch(getCaptchaUrl()); //диспатчим санку, если с сервера resultCode пришел 10 } else { let message = response.data.messages.length > 0 ? response.data.messages[0] : "Some error"; dispatch(stopSubmit("login", {_error: message})); } } } export const getCaptchaUrl = () => async (dispatch) => { const response = await securityAPI.getCaptchaUrl(); const captchaUrl = response.data.url; dispatch(getCaptchaUrlSuccess(captchaUrl)); //диспатчим наш экшнкриэйтер }
*мы можем диспатчить санку с UI, а можем и с другой санки (точнее санккриэйтер внутри другого сакнккриэйтера)
- Следующим шагом является показать пользователю картинку, если url в стейте присутствует. Для этого в Login.jsx:
1) подписываемся в mapStateToProps на captchaUrl;
2) прокидываем через пропсы captchaUrl в нашу редакс-формочку LoginReduxForm;
3) в параметрах (в скобках) компоненты LoginForm через деструктуризацию мы «ловим» из пропсов captchaUrl;
4) через тернарное выражение (2 амперсанта), если captchaUrl, то показываем картинку с капчей;
5) добавляем с аналогичным условием появление поля для воода капчи;
6) добавляем в параметры санккриэйтера login, который повешен через onSubmit на кнопку для авторизации, введенный текст из поля для ввода капчи на ряду с передачей пароля и логина.import React from 'react'; ... const LoginForm = ({handleSubmit, error, captchaUrl}) => { return ( <form onSubmit={handleSubmit}> {createField("Email", "email", [required], Input)} {createField("Password", "password", [required], Input, {type: "password"})} {createField(null, "rememberMe", [], Input, {type: "checkbox"}, "remember me")} {captchaUrl && <img src={captchaUrl} />} {captchaUrl && createField("Symbols from image", "captcha", [required], Input, {}) } {error && <div className={style.formSummaryError}> {error} </div> } <div> <button>Login</button> </div> </form> ) } const LoginReduxForm = reduxForm({ form: 'login' })(LoginForm) const Login = (props) => { const onSubmit = (formData, dispatch) => { //dispatch(reset("login")); props.login(formData.email, formData.password, formData.rememberMe, formData.captcha); } if (props.isAuth) { return <Redirect to={"/profile"} /> } return <div> <h1>LOGIN</h1> <LoginReduxForm onSubmit={onSubmit} captchaUrl={props.captchaUrl} /> </div> } const mapStateToProps = (state) => ({ captchaUrl: state.auth.captchaUrl, isAuth: state.auth.isAuth }) export default connect(mapStateToProps, {login}) (Login);