• Почему webpack ругается на шрифт?

    @SuperOleg39ru
    Front-end разработчик
    Проблема тут - include: path.join(__dirname, 'src/fonts')

    src/sass/materialize/fonts/roboto/Roboto-Regular.woff2 очевидно имеет другой путь)
    Ответ написан
    Комментировать
  • Какое регулярное выражение использовать?

    @SuperOleg39ru
    Front-end разработчик
    /\|([^|]*)\$k/g

    Нужный текст окажется в группе
    Ответ написан
    Комментировать
  • Функция connect() Redux, как правильно использовать?

    @SuperOleg39ru
    Front-end разработчик
    Множество connect компонентов, которые передают из store минимально необходимое количество данных - это верный подход, в таком случае компонент, обернутый в connect, будет обновляться только тогда, когда изменились реальные данные, от которых он зависит.

    По вашей проблеме:

    Представим, что в store у вас есть 2 свойства:
    1) options - массив строк для селекта
    2) selectedOption - выбранная строка (пустая по дефолту)

    И например одно действие - selectOption(option), для обновления selectedOption в store.

    Далее условные обернутые компоненты:
    // Ваш сайдбар обновится при каждом изменении selectedOption
    connect((state, ownProps) => ({ selectedOption: state.selectedOption }))(SidebarComponent)
    
    // Ваш селект обновляется в зависимости от изменения обоих свойств,
    // и используя свойство onSelect может обновить selectedOption самостоятельно
    connect(
      (state, ownProps) => ({ options: state.options, selectedOption: state.selectedOption }),
      (dispatch, ownProps) => ({ onSelect: (value) => dispatch(selectOption(value)) })
    )(SelectComponent)
    Ответ написан
  • Как добавить минимизацию css в webpack?

    @SuperOleg39ru
    Front-end разработчик
    Когда вы добавляете стили в качестве entry, они оборачиваются в js скрипт, который просто вставляет эти стили на страницу, при загрузке.

    Обычный кейс для создания отдельного css бандла:

    // в вашем './src/app.js'
    import './styles/main.sass';
    
    // в конфиге вебпака все надо упростить
    // entry
    {
      app: './src/app.js',
    }
    // module.rules
    {
      test: /\.sass$/,
      use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: [
          'css-loader',
          'sass-loader'
        ]
      })
    }
    // plugins
    new ExtractTextPlugin('styles/[name].css')


    Название entry мы меняем, для читаемости, что бы не делать всякие bundle.bundle.js или bundle.min.js,
    Ваши стили получат такое же имя, как и основной js файл - итого app.min.js и app.min.css
    HtmlWebpackPlugin автоматически подтянет ссылки на эти файлы.
    Ответ написан
    1 комментарий
  • Как решить несовпадение сигнатур при использовании сторонних библиотек?

    @SuperOleg39ru
    Front-end разработчик
    Декларация метода shuffle:
    shuffle<T>(list: _.Collection<T>): T[];

    Возможно, свойство класса this.proposed_options не типизировано как Syllable.

    Перепишите вручную, но используя generic:
    _.shuffle<Syllable>(this.proposed_options)
    Ответ написан
    Комментировать
  • Можно ли писать на React + JSX без Webpack? В чем минусы?

    @SuperOleg39ru
    Front-end разработчик
    https://codepen.io/anon/pen/EwLLbN

    Минусы - браузеру придется компилировать код на лету, это большая потеря в производительности.

    Писать на JSX позволяет babel, а не webpack, поэтому в примере на codepen добавлен скрипт с babel.
    Ответ написан
    Комментировать
  • Как реализовать этот ползунок?

    @SuperOleg39ru
    Front-end разработчик
    1) Треугольная форма прогресс-бара делается бордерами: https://css-tricks.com/snippets/css/css-triangle/

    2) Движение ползунков, ключевые моменты:
    Событие mousedown вы отслеживаете на ползунке либо на прогресс-баре.
    Внутри обработки mousedown вешаете события mousemove и mouseup на document или window (не забудьте их убрать по mouseup)

    Во время mousemove вычисляете положение курсора относительно левого края слайдера (например event.clientX - sliderElement.getBoundingClientRect().left).

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

    Еще, для адаптивности, желательно все из пикселей в % переводить
    Ответ написан
    Комментировать
  • Способен ли Webpack при сборке менять пути API?

    @SuperOleg39ru
    Front-end разработчик
    Вы можете добавлять пути как параметры при билде.

    В конфиге:
    const API = process.env.api;
    
    module.exports = {
        ...,
        plugins: [
            ...,
            new webpack.DefinePlugin({
                API_URL: JSON.stringify(API)
            })
        ]
    }


    И глобальная переменная API_URL будет доступна в коде вашего проекта.

    Условный конфиг для сборки в вашем package.json:
    {
      "scripts": {
        "build": "webpack --env.api=https://api.url/v1"
      }
    }


    Если надо на лету добавить:
    npm run build -- --env.api=https://api.url/v2
    Ответ написан
    Комментировать
  • Как вернуть контекст в данном случае?

    @SuperOleg39ru
    Front-end разработчик
    1 вариант - использовать стрелочные функции:

    method(function() {
    ...
    });
    
    // заменить на 
    method(() => {
    ...
    })


    2 вариант - привязать контекст к переменной:

    const that = this;
    that.context;


    Почитайте это
    Ответ написан
    2 комментария
  • Webpack ошибка, Как исправить?

    @SuperOleg39ru
    Front-end разработчик
    var output = [],
    var loaders = {}


    Замените запятую на точку с запятой.
    Ответ написан
    1 комментарий
  • Как протестить то, что случится после нажатия кнопки в React(Jest, enzyme)?

    @SuperOleg39ru
    Front-end разработчик
    На самом деле вам не нужно это тестировать.

    Разделяйте тестирование частей React + Redux приложения:

    1) Отдельно reducer и соответствующие actions, что делать очень просто, так как это чистые функции и простые объекты.

    2) Отдельно react компонент, и вы должны проверить только то, что при simulate('click') вызовется соответствующий обработчик.
    Пример:
    const props = {
        onClick: jest.fn()
    };
    let component;
    
    beforeEach(() => {
        component = enzyme.shallow(<Button {...props} />);
    });
    
    it('срабатывает событие onClick', () => {
        field.simulate('click');
        expect(props.onClick).toHaveBeenCalled();
    });


    Если вам нужно проверить, отреагировал ли компонент на изменение его свойств, то вы меняете его свойства напрямую через component.setProps({ key: 'value' });
    Ответ написан
    Комментировать
  • React.js. Доставка данных с сервера при первой загрузке и обновлении роута. Есть ли какие-то паттерны?

    @SuperOleg39ru
    Front-end разработчик
    У меня аналогичная ситуация, настраиваю react-router для нового приложения. Сразу скажу, что паттернов, кроме документации, не попадалось)

    В initialState для store вы сами решаете, что надо туда добавлять, в любом случае каждый редьюсер дает свой дефолтный кусочек общего state.

    По переходам - разделите данные на общие, которые нужны до старта приложения, и конкретные для компонентов.

    Про блок перехода - пример из документации, в зависимости от данных сверху, роут либо рендерит компонент, либо делает редирект.
    const PrivateRoute = ({ component: Component, ...rest }) => (
      <Route {...rest} render={props => (
        fakeAuth.isAuthenticated ? (
          <Component {...props}/>
        ) : (
          <Redirect to={{
            pathname: '/login',
            state: { from: props.location }
          }}/>
        )
      )}/>
    )


    Пример компонента, который запрашивает данные. Все свойства через connect, но вы можете так же менять и отслеживать состояние запроса в state.
    // getUsers - свойство из mapDispatchToProps, запрос на сервер, по завершению обновляет users в вашем Store
    // users - свойство из mapStateToProps
    class UserListContainer extends Component {
      componentWillMount() {
        this.props.getUsers();
      }
    
      render() {
        const { users } = this.props;
    
        if (!users) {
          return <Spinner />
        }
    
        return <UserList data={users} />
      }
    }


    В моем приложении я включаю spinner при старте приложения, и выключаю в компоненте, который рендерится после получения нужных данных. Например можно делать так (свойства и методы так же через connect).
    У вас будет крутиться прелоадер, пойдет запрос на токен, по успеху пойдет запрос на users, по успеху начнет рендериться UserListContainer, который должен выключить прелоадер.
    class App extends Component {
      render() {
        const { appIsReady } = this.props;
    
        // наш лоадер на весь экран при appIsReady: false
        return (
            <div>
              {!appIsReady && <Spinner style='fullscreen' />}
              </Switch>
                <Route path='/' exact={true} component={Home} />
                <Route component={NotFound} />
              <Switch>
            </div>
        );
      }
    }

    class Home extends Component {
      componentWillMount() {
        if (!this.props.token) {
          this.props.getToken();
        }
      }
    
      render() {
        const { token } = this.props;
    
        if (!token) {
          return null;
        }
    
        // UserListContainer в willMount меняет appIsReady на true
        return <Route path='/users' component={UserListContainer} />  
      }
    }
    Ответ написан
    Комментировать
  • Почему не работает dispatch в ReactNative?

    @SuperOleg39ru
    Front-end разработчик
    Похоже проблема не в React Native:

    export default function counter(state = initState, action) {
      switch (action.type) {
        case 'COUNTER:INCR':
          console.log('INCREMENT');
          state.num = state.num++;
          return state;
        case 'COUNTER:DECR':
          console.log('DECREMENT');
          state.num = state.num--;
          return state;
        case 'COUNTER:RES':
          state.num = 0;
          return state;
        default:
          return state;
      }
    }


    Вам нужно ознакомиться с принципами Redux. Нельзя изменять хранилище, аргумент state в редьюсере получает store вашего приложения, а в каждом блоке case вы должны возвращать либо не измененный store (default), либо копию вашего хранилища:

    export default function counter(state = initState, action) {
      switch (action.type) {
        case 'COUNTER:INCR':
          return {
            ...state,
            num: state.num + 1
          };
        case 'COUNTER:DECR':
          return {
            ...state,
            num: state.num - 1
          };
        case 'COUNTER:RES':
          return {
            ...state,
            num: 0
          };
        default:
          return state;
      }
    }


    Вместо spread оператора

    {
      ...state,
      num: state.num + 1
    }

    можно использовать
    Object.assign({}, state, { num: state.num + 1 })
    Ответ написан
    1 комментарий
  • С помощью каких технологий лучше разработать визуальный Drag&Drop конструктор?

    @SuperOleg39ru
    Front-end разработчик
    Существуют drag&drop библиотеки и для ванильного js, и для фреймворков.

    Вот топовая библиотека - https://bevacqua.github.io/dragula/

    Вот библиотека для использования с React - react-dnd.github.io/react-dnd

    React или Angular нужны вам для создания аккуратной, модульной и расширяемой кодовой базы. Но если вы ими не пользовались, или опыта немного, конечно в начале их использование притормозит работу, но в конечном счете оно того стоит.
    Ответ написан
    1 комментарий
  • AngularJS как на es5 задать геттер\сеттер у контроллера компоненты?

    @SuperOleg39ru
    Front-end разработчик
    Пример класса на ES5:
    function MyComponent() {
      this._bindedValue = 'someValue';
    }
    
    MyComponent.prototype.getBindedValue = function() {
      return this._bindedValue;
    }
    
    MyComponent.prototype.setBindedValue = function(value) {
      this._bindedValue = value;
      this.makeSomethingWithNewValue();
    }


    Геттеры-сеттеры в ES5 доступны для объектов:
    var MyComponent() {
      _bindedValue: 'someValue',
    
      get bindedValue() {
          return this._bindedValue;
      }
    
      set bindedValue(value) {
         this._bindedValue = value;
         this.makeSomethingWithNewValue();
      }
    }


    Пример подробнее, можно применить и к функции.
    Ответ написан
  • Почему webpack-dev-server не отдаёт бандл для index.hmtl?

    @SuperOleg39ru
    Front-end разработчик
    Webpack-dev-server не пишет собранные файлы на диск, а хранит их в памяти. contentBase говорит только откуда брать статические файлы.

    Надо именно такой адрес - добавьте output.publicPath: '/bundles/'

    Лучше используйте HtmlWebpackPlugin, он сам создаст index.html + добавит ваш index.js на страницу.

    В данный момент ваш index.js находиться виртуально в одной директории с index.html.
    Ответ написан
    Комментировать
  • Angular 1.6.4 $watchGroup возвращает пустые строки для старых значений. Не пойму в чём дело?

    @SuperOleg39ru
    Front-end разработчик
    Рекомендую вам отказаться от $watchGroup и $watch, вместо этого на инпуты добавьте ng-change, и уже в функциях-обработчиках делайте все что вам угодно.

    Причины:
    1) Полностью контролируете процесс
    2) Нет потери производительности из за watch'еров
    Ответ написан
  • [code-review] JS галерея, какие замечания?

    @SuperOleg39ru
    Front-end разработчик
    Добрый день!

    1) Для удобства оформите библиотеку как npm пакет.
    И что бы этот пакет можно было использовать разными системами сборки, можно использовать формат определения модуля UMD

    2) При создании экземпляра const gallery = new Gallery({}) - неплохо бы иметь доступ к управлению галереей через JS, например:
    gallery.pause();
    gallery.next();


    Возможно вам захочется добавлять слайды на лету, это тоже отдельный метод.

    3) Так же, можно добавить изменение настроек на лету, gallery.setOptions({})

    В связи с этим можно сделать вывод, что нужен более объекто-ориентированный код, то есть вы создаете класс Gallery, который имеет методы для управления, сохраняет свойства себе в контекст (this), например:

    var defaultOptions = {
      ...
    }
    
    privateMethod() {
      ...
    }
    
    function Gallery(options) {
      this.options = extend(defaultOptions, options);
      this.interval = this.createInterval();
      ...
    }
    
    Gallery.prototype.init = function() {
      ...
    }
    
    Gallery.prototype.next = function() {
      ...
    }
    
    Gallery.prototype.createInterval= function() {
      ...
    }
    
    function extend(a, b) {
      ...
    }


    То есть, всю логику вы разделяете на отдельные функции, которые являются методами класса Gallery, если нужен публичный доступ к этой логике, либо выносите эти методы и свойства наружу, если их нельзя менять пользователям.
    Ответ написан
    3 комментария
  • React и асинхронные запросы?

    @SuperOleg39ru
    Front-end разработчик
    Для работы с API удобно будет взять готовую библиотеку, у которой методы http запросов возвращают промисы или объекты с аналогичными свойствами.
    Я могу посоветовать axios, в простом случае хватит и нативного метода fetch.

    Затем, вы можете создать отдельные классы - сервисы для работы с конкретными разделами API (например UsersService для работы со списком пользователей, AuthService для авторизации). И методы сервиса принимают необходимые аргументы, и возвращают методы axios (например UsersService.get(id) возвращает axios(`api/url/users/${id}`))

    И эти сервисы вы используете в ваших "умных" компонентах, в методе componentDidMount.

    Ссылка на статью с примером тут
    Ответ написан
    Комментировать
  • Для чего нужен react-router-redux?

    @SuperOleg39ru
    Front-end разработчик
    It is useful if you care about recording, persisti...

    Коротко - если используете Redux dev tools, хотите отслеживать там перемещения по роутам - используйте react-router-redux.
    Ответ написан
    4 комментария