• Как в react redux обновлять access token?

    @allbidder

    При использовании axios(interceptors) - можно сделать так:


    redux/store.js
    import { createStore, applyMiddleware, compose } from 'redux';
    import axios from 'axios';
    import axiosMiddleware from 'redux-axios-middleware';
    
    import API from 'config/api';
    import { isTokenExpiredError, resetTokenAndReattemptRequest } from 'helpers/Authorization';
    import reducer from './reducers';
    
    const client = axios.create({
      baseURL: API.baseUrl,
      responseType: 'json',
      headers: {
        'Content-Type': 'application/json',
      },
    });
    
    client.interceptors.response.use(null, (error) => {
      if (isTokenExpiredError()) {
        return resetTokenAndReattemptRequest(error);
      }
      return Promise.reject(error);
    });
    
    const apiMiddlewareDefault = [axiosMiddleware(client)];
    
    const store = createStore(
      reducer,
      compose(
        applyMiddleware(...apiMiddlewareDefault),
        // window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
      ),
    );
    
    export default store;


    helpers/Authorization.js
    import API from 'config/api';
    import STORAGE from 'helpers/storage';
    
    import axios from 'axios';
    
    const getTokenStorage = STORAGE.getItem('token');
    let isAlreadyFetchingAccessToken = false;
    let subscribers = [];
    
    export function setAuthBearerToken(token) {
      if (token) {
        axios.defaults.headers.common.Authorization = `Bearer ${token}`;
      } else {
        delete axios.defaults.headers.common.Authorization;
      }
    }
    
    export function isTokenExpiredError() {
      const accessTokenExpires = getTokenStorage && STORAGE.jwtDecode(getTokenStorage.access_token);
      const getTime = new Date().getTime() / 1000;
      return accessTokenExpires.exp <= getTime;
    }
    
    function onAccessTokenFetched(accessToken) {
      subscribers.forEach(callback => callback(accessToken));
      subscribers = [];
    }
    
    function addSubscriber(callback) {
      subscribers.push(callback);
    }
    
    export async function resetTokenAndReattemptRequest(error) {
      try {
        const { response: errorResponse } = error;
        const resetToken = getTokenStorage.refresh_token;
        if (!resetToken) {
          return Promise.reject(error);
        }
    
        const retryOriginalRequest = new Promise((resolve) => {
          addSubscriber((accessToken) => {
            errorResponse.config.headers.Authorization = `Bearer ${accessToken}`;
            resolve(axios(errorResponse.config));
          });
        });
    
        if (!isAlreadyFetchingAccessToken) {
          isAlreadyFetchingAccessToken = true;
          const response = await axios({
            method: 'post',
            url: `${API.baseUrl}/refresh`,
            data: {
                refresh_token: resetToken,
            },
          });
    
          if (!response.data) {
            return Promise.reject(error);
          }
    
          const newToken = response.data.access_token;
          setAuthBearerToken(newToken);
          onAccessTokenFetched(newToken);
          STORAGE.setItem('token', {
            access_token: newToken,
            refresh_token: response.data.refresh_token,
          });
          isAlreadyFetchingAccessToken = false;
        }
    
        return retryOriginalRequest;
      } catch (err) {
        return Promise.reject(err);
      }
    }


    helpers/storage.js
    import jwt from 'jsonwebtoken';
    
    class STORAGE {
      static setItem = (key, value) => localStorage.setItem(key, JSON.stringify(value));
    
      static getItem = key => JSON.parse(localStorage.getItem(key));
    
      static removeItem = key => localStorage.removeItem(key);
    
      static clear = () => localStorage.clear();
    
      static jwtDecode = token => jwt.decode(token);
    }
    
    export default STORAGE;
    Ответ написан
  • Требования к вёрстке у Junior Front end( в будущем реакт разраб)?

    Xuxicheta
    @Xuxicheta
    инженер
    Поймите одно, у нас нет стандартов профессии. Нанимают не "человека по стандартам", нанимают тех, кто решает задачи бизнеса, а они разные.
    Если у фирмы есть верстальщик, фронт возможно вообще не будет верстать. А иногда наоборот, от фронта требуют идеальных макетов. Как повезет. У мидлов и выше есть еще возможность выбирать себе работу и компанию, джун будет делать то, что скажут.
    Можешь попробовать сделать свои пять макетов и браться за js, но к реальности тебя вернут только собеседования.

    энивей, нормальный фронт уж несколько месяцев своей жизни на верстку потратил почти наверняка.
    Ответ написан
  • Нужно ли помещать в стор все состояния компонента?

    Robur
    @Robur
    Знаю больше чем это необходимо
    Все состояния не нужно.

    Состояния компонента кладете в стейт компонента, состояния данных - в стор.
    Скорее всего источник вашего смущения в том что вы думаете что состояния загрузки данных - это состояние компонента, а это не так, это как раз относится к данным - данных нет, данные загружаются, данные загружены, данные загружены с ошибкой - это все не про UI. То что они "временные"ничего не значит. Информация о том что в происходит загрузка данных - это тоже данные.
    Это может вообще не показываться в UI или же показываться сразу во многих компонентах - не важно.

    UI просто может отображать эти состояния каким-то образом, так же как он отображает данные когда они в сторе появляются.
    Ответ написан
  • Как предотвратить выход с сайта из-за history.goBack()?

    @Raipon
    Я бы проверил history.length и вызывал history.goBack(), если есть куда возвращаться.
    Ответ написан
  • Что значит "throw new Error('oops')"?

    2chevskii
    @2chevskii
    Если бы каждый мог в поиск, тостер бы вымер.
    Учите язык, а не задавайте вопросы на форумах - это просто бессмысленно.
    Ответ написан
  • Полноценный пример SSR для react/redux?

    Да состояние собирается на сервере для каждого клиента (request'a), скажем на уровне мидлвара мы собираем состояние (текущего авторизованного пользователя, какие-то другие глобальные данные), далее отрабатывает обработчик маршрута, мы получили данные какой-то страницы из бд и передали их как контекст, примерно так:
    import React from 'react';
    import { StaticRouter } from 'react-router'
    import { Provider } from 'react-redux'
    import ReactDOMServer from 'react-dom/server';
    
    import App from './client/components/App.jsx'
    
    ReactDOMServer.renderToString(
    	<Provider store={ReduxStore}>
    		<StaticRouter
    			location={Url}
    			context={Context}>
    			<App/>
    		</StaticRouter>
    	</Provider>
    );

    Где, ReduxStore сгенерированное нами глобальное состояние (redux) запроса, Url запрошенный урл, Context контекст (будет передано как this.props.staticContext в компонент). Реакт вытянет нужный контейнер роута (по вашим маршрутам в App) и передаст ему контекст, компонент рендерится исходя из полученных данных. Результатом работы метода renderToString будет html строка (размеченная реактом), которую мы шаблонизатором или как угодно впиливаем в блок моунта компонента (в верстке), дополнительно в шаблонизатор передаем сгенерированное состояние, в документации выглядит вот так:
    window.__PRELOADED_STATE__ = JSON.stringify(preloadedState || {}).replace(/</g, '\\u003c')

    Теперь что происходит после того как страница загрузилась и подхватились клиент-скрипты? Все просто мы подхватываем состояние из window.__PRELOADED_STATE__ и вообщем-то все, глобальное состояние передано, компонент уже отрендерен, стоит учитывать что результаты при клиент-рендере и при сервер-рендере должны быть всегда одинаковыми, так же не использовать методы доступные браузеру, но не доступные серверу (на уровне моунта и первого рендера) и хорошенько следить за своим кодом в плане памяти, иначе при какой-либо утечке, память на сервере не будет вычищаться после каждого рендера.
    ---
    Как-то так, надеюсь помог, хотя там еще довольно много заковык
    Ответ написан
  • Про перерендер и обновление состояния в react?

    kshshe
    @kshshe
    Frontend developer
    Можно так: хранить значение чекбокса и флаг и его состояния (сохраняется сейчас, сохранен, предыдущее состояние). Когда значение меняется, рисовать сразу, записывать предыдущее состояние, ставить флаг «сохраняется», а при завершении запроса либо менять на «сохранено», либо менять значение обратно и выводить ошибку.
    Ответ написан
  • Как лучше писать стили CSS Modules или Styled Components?

    @grinat
    То и то отвратительно, благодаря обычному css реально можно просто и быстро стилизовать страницы. А это все калька с десктопных приложений, а их стилизация всегда была кошмаром, попытка добавить какое-нить скругление превращалось в борьбу с кучей xml'ек. Теперь же когда десктоп почти умер, поскольку все массово переходят в вэб, народ пытается перетащить худшие вещи оттуда.
    Ответ написан
  • Cookie или localStorage?

    @artinnok
    бекенд-программист
    Тут нет оптимального варианта - надо смотреть на бэкэнд и фронтенд совокупно.
    Далее, считаем что фронтенд SPA, бэкэнд чистое API.

    Если выбор за Cookies:
    1. На сервере необходимо реализовать защиту от CSRF
    2. На фронте также надо будет реализовать обвязку под передачу CSRF-токена
    3. Вся авторизация будет обрабатываться бэкэндом - т.е. он ставит куку через Set-Cookie с httpOnly и secure и сам же читает из куки для аутентификации / авторизации
    4. Фронтенд не имеет доступа к кукам - не думает об авторизации и об XSS
    5. Обычно, XSS используют чтобы украсть авторизационный токен через JS - если авторизационная кука httpOnly - нет доступа к кукам через JS, нет проблем

    Если выбор за localStorage:
    1. Не надо делать CSRF защиту на сервере - нет кук, нет проблем
    2. Экранировать потенциально опасные данные бэкэндом на входе - обеспечит дополнительную защиту от XSS при рендере данных
    3. Надо делать защиту от XSS на фронте, т.е. экранирование данных при рендере - чтобы потенциально вредоносный JS код превращался в строку, а не вставлялся в DOM
    4. На фронте надо сделать обвязку под сохранение / передачу / валидацию токена
    5. Всегда остается человеческий фактор - забыли сделать экранирование при рендере на фронте, JS код имеет доступ к токену и он утек


    Могу посоветовать использовать фреймворки, как на бэкэ, так и на фронте - они обычно имеют уже реализованную защиту от основных типов атак. Мы на проекте юзаем localStorage и React, ну и верим в лучшее :)
    Ответ написан
  • Как правильно реализовать mvc?

    @grinat
    У тебя есть некий class App который стартует при запросе, и в нем лежит Router, который обрабатывает запрос, разбирает /siski либо index.php?url=siski и решает какой контролер вызывать: SiskiContoller.php либо SiskiAjaxController.php когда запрос приходит в контролер, тот забирает данные из модели Siski, SiskiContoller данные ренредрит в файл siski который лежит во вьюхах, SiskiAjaxController делает json_encode для данных и отображает.
    Ответ написан
  • Почему Axios не видит файл?

    @imhuman
    Вы уверены, что запись в файл делается именно так?
    Ответ написан
  • Почему Axios не видит файл?

    Vlad_IT
    @Vlad_IT Куратор тега JavaScript
    Front-end разработчик
    Вы не можете через клиент (JS) заносить напрямую данные на сервер. На сервере должен быть скрипт, который принимает ваши данные, проверяет их и сохраняет в файл.
    Ответ написан
  • Почему Axios не видит файл?

    Это так не работает. На стороне сервера должен быть обработчик POST запроса, который обновит содержимое файла
    Ответ написан
  • Как передать массив отмеченных chechbox в форме с помощью AJAX?

    profesor08
    @profesor08 Куратор тега JavaScript
    Задай разные имена чекбоксам, это тебе не радио кнопка. На это тебя должно было навести то, что в $_POST у тебя только один ключ "checkbox", потому что в массиве может быть только один уникальный ключ, следовательно значение по ключу будет перезаписываться. Ответ на вопрос лежит на поверхности, но по незнанию самых базовых вещей, ты пытаешься выдумывать какие-то решения проблемы.
    Ответ написан
  • Как сделать правильный выбор между C# и Java?

    Sputterspark
    @Sputterspark
    В далеком прошлом, я занимался пустой тратой времени. А именно - выбором языков программирования.

    И продолжаешь этим заниматься.
    Ответ написан
  • Какова суть фреймворков и библиотек?

    Stalker_RED
    @Stalker_RED
    Библиотека это инструмент или набор каких-то инструментов.
    Бибилиотека для скачивания видео с ютуба
    Бибилиотека для кропа и ресайза картинок
    Бибилиотека для определения города по IP

    Фреймворк может использовать десятки или сотни разных библиотек. Это набор не только инструментов, но и готовых компонентов, и еще и рекомендация по их использованию.

    "набор для постройки скворечника"
    В комплекте молоток, гвозди, столярный клей, 20 деревянных досточек разных форм и расцветок и инструкция с тремая вариантами скворечника на выбор.

    Или вот два фреймворка:
    Ezva9I.pngzC6ZHT.png
    Можно ли их использовать вместе? (Конечно, никто не запрещает)
    Можно ли из этих деталей построить что-то совсем другое, не такое как в инструкции? (Конечно да)
    Можно ли с этими фреймворками использовать детали еще и из этого?
    lGjE1A.png
    (конечно можно, но придется что-то придумать для совместимости деталек. Быть может придется применить клей, изоленту, пластилин или жвачку. Или шуруповерт, или сварочный аппарат. Но ни в один комплект эти дополнительные инструменты не входят, как и скиллы к ним.)

    Можете посмотреть еще сюда, этот ответ частично покрывает ваш вопрос:
    Для чего нужны фреймворки, а-ля Laravel?
    Ответ написан
  • Как ограничить кол-во запросов в секунду в Telegram боте?

    @BorisKorobkov Куратор тега PHP
    Web developer
    Вам надо не "ограничить кол-во запросов", а изучить, что такое "race condition".
    Ответ написан
  • JSON - есть ли разница между {'num': 1} и {num: 1}? Как лучше писать?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Нет разницы - оба варианта некорректные, таких JSON'ов не бывает.

    Конечно, на самом деле вы имели в виду кое-что другое, но... учитесь задавать вопросы.
    Ответ написан