• А какая архитектура reactjs/redux приложения у вас?

    Отказался от деления компонентов на умные и глупые. Любому компоненту может потребоваться прямой доступ к данным, и перекидывать лапшу в props - неблагодарное занятие. У меня в целом все просто: компоненты в папке Components, редьюсеры в папке Reducers, экшены в Actions, константы в Constants, и все остальное по назначению.
    Ответ написан
    6 комментариев
  • Где грань между версткой и front-end?

    rockon404
    @rockon404
    Frontend Developer
    Где грань, когда можно/нужно обойтись нативными средствами что бы меньше зависеть от либы/фреймворка/js и когда необходимо использовать front-end либы?

    Когда от верстания макетов перейдете к реальным задачам, вроде реализации какого-нибудь сервиса(социальная сеть, личный кабинет банка, сервис бронирования, служба доставки с личным кабинетом, видео конференции и тп), тогда и поймете.

    Для верстки лендосов тянуть фронтенд фреймворки нет особого смысла.
    Второй макет можно реализовать на React/Vue как красивое одностраничное приложение с клиентским роутингом. Там и бронирование отеля и магазин. Думайте не о верстке, а о том как такие вещи как бронирование и магазин реализовывать будете. Тогда может и придет понимание зачем во фронтенд разработке используют фреймворки и где та самая грань.

    Но с другой стороны есть повторяющиеся элементы, те же слайдеры от блока к блоку - можно было бы в react-овский HOC засунуть логику слайдера (это что касается второго макета).

    HOC используют не для этого.

    И в принципе с create-react-app или аналогом от vue было бы удобнее работать чем со сборкой на webpack

    Пока верстаете макеты и не работали в реальных проектах может быть и удобней. И да, create-react-app и аналог от Vue под капотом используют webpack.
    Ответ написан
    Комментировать
  • Стоит ли идти в программисты в 30 лет, но не простым, а..?

    iCoderXXI
    @iCoderXXI
    React.JS/FrontEnd engineer
    Фронт года с 14 усиленно превращается в энетрепрайз, со всеми теми же проблемами, что и в любом другом энтерпрайзе. На скольких проектах побывал - в каждом свой зоопарк и свои представления о прекрасном, часто еще и не полностью пересекающиеся с моими. В результате вместо ударного креатива я вынужден заставлять себя писать в манере, требуемой заказчику, что далеко не всегда получается легко и просто. Много сил уходит на компенсацию внутренних конфликтов, типа душа стремится к прекрасному и совершенному, а писать приходится то что приходится... От этого выгорание происходит ударными темпами, а требования к программистам формируют явно люди, которые ни строчки кода в жизни не написали и даже приблизительно не понимают, как это работает, какова физиология нервной системы и чем чреваты такие неадекватные требования.

    При этом пока ты молод и один, в целом зарплаты тебе хватает чтобы жить симпатичненько, позволять себе макбучики и айфончики и еще на дошики останется... А если, не приведи господь, у тебя семья, ипотека и пр., то уровень жизни объективно стремится к прожиточному минимуму и каждый день перманентный стресс. Ни о каких накопления речи вообще при таком раскладе не идет. Креатив тоже сходит на нет. Хорошо если есть рефлексы, выработанные годами, которые позволяют это безобразие худо-бедно вывозить. А если нет?

    Да еще, за последние 5 лет фронтенд дичайше усложнился, обогатился тоннами технологий и эти процессы продолжают победное шествие ударными темпами. А это означает, что ты просто обречен бежать впереди паровоза, иначе вон с рынка. Ну или велкам обслуживать полчища легаси проектов с лютейшим говнокодом в обмен на еду...

    Все вышесказанное полнейшее ИМХО. Возможно просто я сегодня встал не с той ноги... Кто его знает...
    Ответ написан
    1 комментарий
  • А какая архитектура reactjs/redux приложения у вас?

    nakree
    @nakree
    Fullstack Developer
    src/
    --utils/
    --config/
    --css/
    --img/
    --components/
    ----User/
    ------User.jsx
    ------UserContainer.js
    ------UserReducer.js
    ------UserActions.js
    Ответ написан
    2 комментария
  • А какая архитектура reactjs/redux приложения у вас?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Есть два хороших подхода к организации кодовой базы, которые подходят для большинства проектов: File Type First и Feature First:

    Пример Feature First

    Проект:
    /common
      /api
      /components
      /ducks
      /entities
      /sagas
      /selectors
      /utils
    /features
      /Feature1
      /Feature2
      /Feature3
      /Feature4
      ...
      /FeatureN
    /Main
      /pages
      index.js
      App.js
      routes.js
      rootReducer.js
      rootSaga.js
      store.js
    /Auth
      /pages
      index.js
      App.js
      routes.js
      rootReducer.js
      rootSaga.js
      store.js
    ...

    Отдельно взятая Feature:
    /features
      /Accounts
        /components
        index.js
        accountsDucks.js
        accountsSaga.js
        accountsSelectors.js
        accountsApi.js
        Accounts.js
        AccountsContainer.js

     
    Пример File Type First
    /actions
    /common
    /components
      /core
      /Feed
      /Profile
      ...
    /constraints
    /containers
    /entries
    /locales
    /pages
    /reducers
    /utils
    ...


    Оба подхода при умелом использовании они отлично масштабируются, поддерживаются и не вызывают проблем при рефакторинге.

    Дата мапперы в зависимости от задач можно использовать в редьюсерах, в mapStateToProps и asyncActions. Главное чтобы по проекту все было стандартизировано.
    В mapStateToProps пишут преобразования необходимые лишь для одного компонента.

    Большое количество бойлерплейта это плата которую вы платите за использование redux. Можно писать все константы и редьюсеры руками, можно использовать библиотеки вроде redux-actions и ей подобные. В первом случае вы получаете плюс к гибкости, читаемости и статическому анализу, во втором меньше кода. Я в большинстве проектов предпочитаю первый вариант. Так же создаю шаблоны файлов в Webstorm для asyncActions, contstraints, редьюсера, страницы, компонента и законнекченого компонента.
    В специфичных проектах с множеством CRUD запросов и похожих сущностей есть смысл написать CRUD Boilerpalte.
    Ответ написан
    Комментировать
  • Как реализовать авторизацию пользователя на стеке React Redux Node?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Пригодится jwt. (гугл)

    В общих словах: вам нужно выдать юзеру токен. С этим токеном юзер будет выполнять запросы к вашим защищенным методам api (вы можете оставить какие-то методы "без токена").

    Node.js часть
    Пример роута на логин (сделано не лучшим образом, в плане коллбэков, но что нашлось под рукой...):

    auth.js
    ...
    const v4 = require('node-uuid').v4
    const jwt = require('jsonwebtoken')
    ...
    router.post('/signin', (req, res, next) => {
    
      // валидация, например... 
    
      if (errors) {
        return res.status(400).json({ errors })
      } else {
        // поиск юзера в базе, сравнение хэша и прочие необходимые операции
        ...
       // допустим все ок, нужно ответить токеном
      // генерируем необходимые опции и сам токен
    
            const payload = {
              _id: user._id,
              iss: 'http://localhost:3000',
              permissions: 'poll',
            }
            const options = {
              expiresIn: '7d',
              jwtid: v4(),
            }
            const secret = new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64')
            jwt.sign(payload, secret, options, (err, token) => {
              // отвечаем токеном, для будущих запросов с клиента
              return res.json({ data: token })
            })
         ...
    })
    
    module.exports = router;


    Пример защищенного роута (метода, который требует токен):
    ...
    const jwt = require('jsonwebtoken')
    const Poll = require('../models/poll')
    ...
    
    const requireToken = (req,res,next) => {
      const token = req.headers['x-api-key']
      const secret = new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64')
    
      jwt.verify(token, secret, (err, decoded) => {
        if (err) {
          return res.status(401).json({ error: err.message })
        }
        req.params.userId = decoded._id
        next()
      })
    }
    ...
    
    // обратите внимание на requireToken - функция вызывается при каждом обращении к роуту
    // то есть при каждом PUT запросе по адресу (например: PUT api/v1/products/1238914)
    // будет вызываться requireToken
    
    router.put('/:id', requireToken, (req, res, next) => {
      const { productId } = req.body
      const userId = req.params.userId
    
      Poll.findById(req.params.id)
        .populate({ path: 'products' })
        .exec((err, poll) => {
            // ... необходимые действия в базе, в процессе успешного зачтения голоса...
        }
    })


    Клиентское приложение:

    Типичный ActionCreator (в качестве библиотеки для запросов можно взять bluebird, isomorphic-fetch, да хоть нативный xhr)
    actions/LoginActions.js

    export function login(data) {
      return dispatch => {
        dispatch({ type: LOGIN_REQUEST })
    
        // request в данном случае - https://github.com/KyleAMathews/superagent-bluebird-promise
        return request.post(`${API_ROOT_V1}/auth/signin`)
          .send(data)
          .then(res => {
            if (!res.ok) {
              dispatch({ type: LOGIN_FAILURE })
            } else {
              dispatch({
                type: LOGIN_SUCCESS,
                data: res.body.data,
              })
              //сохранение токена в localStorage
              localStorage.setItem('cks_token', res.body.data)
            }
          }, err => {
            dispatch({ type: LOGIN_FAILURE })
          })
      }
    }


    Пример голосования с токеном (выше, в node.js части был метод апи, а это PUT запрос с клиента)

    export function vote(productId, voteId) {
      return dispatch => {
        dispatch({ type: POLL_VOTE_REQUEST })
    
        return request.put(`${API_ROOT_V1}/api/v1/vote/${voteId}`)
          .set('X-API-Key', localStorage.getItem('cks_token')) // установка ТОКЕНА в заголовок 'X-API-Key'
          .send({ productId })
          .then(res => {
            if (!res.ok) {
              dispatch({ type: POLL_VOTE_FAILURE })
            } else {
              dispatch({
                type: POLL_VOTE_SUCCESS,
                data: normalize(res.body.data, schema.poll),
              })
            }
          }, err => {
            dispatch({ type: POLL_VOTE_FAILURE })
          })
      }
    }


    Надеюсь код формы с кнопкой "голосовать" не трубется.
    ====

    Все это можно организовать удобнее, например, выставлять токен всем запросам автоматически (речь про клиентскую часть):
    import fetch from 'isomorphic-fetch'
    ...
    const defaultHeaders = {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    }
    
    function buildHeaders() {
      const authToken = localStorage.getItem('TOKEN')
      return { ...defaultHeaders, Authorization: authToken }
    }
    ...
    export function httpPost(url, data) {
      const body = JSON.stringify(data)
    
      return fetch(url, {
        method: 'post',
        headers: buildHeaders(),
        body: body,
      })
      .then(... код оработки запроса ...)
    }


    ===

    Проверить наличие токена, можно с в роутере, с помощью хука на onEnter.

    root.js
    ...
    <Provider store={store}>
          <Router history={routerHistory}>
            {configRoutes(store)} // Роуты создаются функцией, чтобы можно было использовать внутри нее store.getState()
          </Router>
        </Provider>
    ...


    routes.js
    export default function configRoutes(store) {
      function _ensureAuthenticated(nextState, replace, callback) {
        const { dispatch } = store
        const { session } = store.getState()
        const { currentUser } = session
        let nextUrl
    
        if (!currentUser && localStorage.getItem('cks.token')) {
          dispatch(getCurrentAccount())
        } else if (!localStorage.getItem('cks.token')) {
          replace('/signin')
        }
        callback()
      }
    
      return (
        <Route path='/' component={App}>
          <Route path='/signin' component={SigninContainer} />
          <Route path='/signup' component={SignupContainer} />
    
          <Route path='/locked' component={AuthenticatedContainer} onEnter={_ensureAuthenticated}>
            <Route component={LockedArea}>
              <Route path='/locked/a' component={A} />
              <Route path='/locked/b/:id' component={B} />
              <Route path='/locked/c' component={C} />
            </Route>
          </Route>
        </Route>
      )


    Остается только создать AuthenticatedContainer в котором проверять: есть ли currentUser (в рамках этого примера) и если нет - возвращать false. А если есть - this.props.children (в которых будут дальнейшие роуты..)
    ===

    Итого:
    1) у вас есть API написанное на node.js.
    2) У этого API есть защищенные методы, которые проверяют наличия токена в http-запросе. Токен выдается при запросе на логин. Само собой, операция логина (то есть POST запрос на your-api/login) не требует токена.
    3) После удачного логина, вы получаете в ответе на свой запрос токен (это уже внутри клиентского кода, и если мы говорим о redux - то внутри асинхронного вызова в actions)
    4) Сохраняете токен (например, в localStorage)
    5) Во все необходимые http-запросы устанавливаете заголовок с токеном
    Ответ написан
    15 комментариев
  • Объясните простыми словами как работает Redux?

    У вас есть одно большое дерево, в котором хранится все состояние (state) приложения - это хранилище (store).
    Также у вас есть набор редьюсеров (которые скомбинированы в один общий rootReducer) - это функции, который принимают текущее состояние и действие и возвращают новое состояние:
    function someReducer(state = initialState, action) {
      // обычно выглядит как switch 
      // action - простой js-объект
      //              и обязательно имеет строковое поле type
      switch(action.type) {
        // обрабатываем действие с типом SOME_ACTION_NAME
        case 'SOME_ACTION_NAME':
          // берем какие-то данные из экшена и возвращаем новое состояние
          // при этом менять sate нельзя!
          // state.someProperty = action.newStateData <--- НЕТ!
          return { ...state, action.newStateData };
        // Если мы не обрабатываем действие - просто возвращаем старое состояние
        default:
          return state;
      }
    }


    Также есть экшен креаторы (actionCreators) - это функции, которые возвращают действие. затем это действие вещается в хранилище (диспатчится). Типичный пример:
    function someActionCreator(someArg) {
      return {
        type: 'SOME_ACTION_NAME',
        newStateData: someArg + 5, // <-- разная логика
      };
    }


    По-умолчанию в качестве экшена мы можем вернуть только простой объект, но при создании хранилища можно добавить так называемый middleWare. Это специальные функции, которые принимают все экшены из диспатча и могут передавать их дальше (при этом содержат дополнительную логику).

    Если мы хотим получить доступ к состоянию в экшен креаторе - воспользуемся thunkMiddleware:
    import thunkMiddleware from 'redux-thunk';
    
    function createStore(initialState) {
      const reducer = combineReducers(reducers);
      const finalCreateStore = applyMiddleware(
        thunkMiddleware // <-- добавляем middleware
      )(defaultCreateStore);
      return finalCreateStore(reducer, initialState);
    }


    Теперь мы можем делать так:
    function someActionCreator(someArg) {
      return (dispatch, getState) => { // <-- возвращаем фукнцию, а не объект!
        const someState = getState().reducerName;
        return {
          type: 'SOME_ACTION_NAME',
          newStateData: someArg + someState, 
        };
      };
    }


    В общем схема выглядит так:

    actionCreator --action--> dispatch --action--> middleware --action--> store --action--> reducer --> newState


    Затем мы берем из react-redux метод connect, который подключает Ваш умный компонент к хранилищу:
    import { connect } from 'react-redux';
    import { bindActionCreators } from 'redux';
    
    class MyComponent extends Component {
      static propTypes = {
        someProp: PropTypes.string.isRequired,
        someFunc: PropTypes.func.isRequired,
      };
    }
    
    // Тут мы берем из глобального состояния необходимую нам часть
    // В ownProps - свойства компонента. Тут могут быть например свойства от роутера
    function mapStateToProps(state, ownProps) {
      return {
        someProp: state.someReducer,
      };
    }
    
    function mapActionsToProps(dispatch) {
      return bindActionCreators ({ // <-- биндим все на disptach для удобства
        someFunc: (someArg) => someActionCreator(someArg + 1),
      }, dispatch);
    }
    
    export default connect(
      mapStateToProps,
      mapActionsToProps
    )(MyComponent);
    Ответ написан
    3 комментария
  • Как сделать что бы в input type="text" введенное значение сохранялось после обновления страницы?

    3Lvcz
    @3Lvcz
    Фронтенд разработчик
    Писать в sessionStorage: https://developer.mozilla.org/ru/docs/Web/API/Wind...

    Если не подходит по времени жизни, то в localStorage:
    https://developer.mozilla.org/ru/docs/Web/API/Wind...

    Если нет поддержки в нужных тебе браузерах, то в куки:
    https://developer.mozilla.org/ru/docs/Web/API/Docu...

    Библа для удобной работы с куками:
    https://www.npmjs.com/package/js-cookie
    Ответ написан
    Комментировать
  • Почему не вызывается метод из другого метода в компоненте?

    @Mariik
    ТУт все дело в том, как Вы используете функции. Все дело в том, что Function Declaration, Function Exoression, Arrow Function по разному оббрабатывают контекст (this).

    В примере Вяечслава срабатывает потому, что у стрелочной функции () =>
    нет своего контекста, те функция срабатывает в контексте класса. А если Вы используете просто function(){} то у этой функции переданной в Event Loop (грубо говоря очередь асинхронного кода) this теряется и начинает ссылаться на глобальный обьект, те уже на window. Ну а у обьекта window как Вы понимаете действительно нет этих методов.

    В общем тут конечно можно долго отвечать, но надеюсь основная идея ясна.
    Ответ написан
    1 комментарий
  • Почему не вызывается метод из другого метода в компоненте?

    @Firik67
    Middle PHP Developer
    Не сильно хорош в js, но вывод примерно такой - когда вы передаете в качестве колбэка this.geoSuccess, то после вызова this.geoSuccess меняется контекст this, в котором уже нет ваших свойств и методов.
    У меня заработало вот так:
    //our root app component
    import {Component, NgModule, OnInit} from '@angular/core';
    import {BrowserModule} from '@angular/platform-browser';
    import {HttpClient, HttpClientModule} from '@angular/common/http';
    import {Observable} from 'rxjs/Observable';
    
    @Component({
      selector: 'my-app',
      template: `
        <div>
          {{weatherData}}
        </div>
      `,
    })
    class App implements OnInit{
      weatherData: any;
      apiKey: string;
      
      constructor(private http: HttpClient){
        this.apiKey = 'cfcff50de820f9f3e1e5e655e5949edd';
      }
    
      /* при инициализации проверяем поддержку геолокации, 
      *  если есть поддержка - управление переходит в this.geoSuccess
      *  так же функции this.geoSuccess в качестве аргумента передается объект с данными о геолокации
      */
      ngOnInit(){
        if(navigator.geolocation){
          navigator.geolocation.getCurrentPosition(position => this.geoSuccess(position), this.geoError);
        }
      }
    
      /* 
      *  Ф-ция получает аргумент url и выполняет по нему get запрос
      */
      getCurrentWeather(url:string): Observable<any>{
        return this.http.get(url);
      }
    
      geoError(){
        console.log("Unable to retrieve your location");
      }
      
      /* Здесь составляется url для запроса погодных данных по местоположению;
      *  Так же вызывается ф-ция this.getCurrentWeather которой аргументом предаем url и осуществляем get запрос по данному url
      *  подписываемся на ф-цию и получив данные, пишем их в переменную weatherData
      */
      geoSuccess(position){
        let geoApiUrl = 'http://api.openweathermap.org/data/2.5/weather?lat='+ position.coords.latitude + '&lon=' + position.coords.longitude + '&APPID=' + this.apiKey;
        this.getCurrentWeather(geoApiUrl).subscribe(data => {
          this.weatherData = data;
        });
      }
    }
    
    @NgModule({
      imports: [ BrowserModule, HttpClientModule ],
      declarations: [ App ],
      bootstrap: [ App ]
    })
    export class AppModule {}

    Надеюсь, более опытные товарищи объяснят подробнее:)
    Ответ написан
    1 комментарий
  • Какую книгу по go выбрать?

    un1t
    @un1t
    Не книга, но хорошее пособие - https://gobyexample.com/ (вроде перевод есть, тут gobyexample.ru, но его не смотрел)
    Ответ написан
    2 комментария
  • Как предать значение через jq в src=""?

    mrusklon
    @mrusklon
    Не получается? Яростно гугли!
    $("#gavno").attr("src", tvojo_govno);
    Ответ написан
    6 комментариев
  • Как при скролле добавить класс с помощью VueJs?

    @RaulDuke
    Добрый день.

    <div id="app" @scroll="letsScroll = true" :class="letsScroll && '_scrolled'">


    https://jsfiddle.net/olegcvetkov76/hjvh2n76/
    Ответ написан
    Комментировать
  • Почасовая/фиксированная оплата и повышение скилла на задачах проекта?

    @ya_yshel_rabotati_v_teleg
    У меня была постраничная оплата за страницу 2000 делал по 3 страници в день потом количество задач начало сокращаться и я договорился с дириком о том что в день у меня 1500 + то что за странички он это воспринял вполне нормально
    Ответ написан
    Комментировать
  • Почасовая/фиксированная оплата и повышение скилла на задачах проекта?

    opium
    @opium
    Просто люблю качественно работать
    ну попросите что вам мешает то
    Ответ написан
    Комментировать
  • Почасовая/фиксированная оплата и повышение скилла на задачах проекта?

    Moon_Lobster
    @Moon_Lobster
    Инвестор
    Пообщайся открыто с дириком как расписал здесь, твои запросы вполне корректны.
    Ответ написан
    Комментировать
  • Сетка блоков, которые меняются местами произвольно?

    @rejjer
    У этих же ребят есть Isotope (метод shuffle).
    А так - да, довольно странно будет видеть такое поведение.
    Ответ написан
    1 комментарий
  • Почему многие ругаются на flex и float? Что в таком случае использовать?

    Флекс атуален на все 100, он поддерживается даже IE11
    https://css-tricks.com/snippets/css/a-guide-to-flexbox/
    Грид это ближайшее будущее, его нужно пробовать
    https://css-tricks.com/snippets/css/complete-guide...
    Ответ написан
    Комментировать