Ответы пользователя по тегу JavaScript
  • Как сделать отзывчивое изображение?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Сделайте обертку, туда передавайте нужные размеры (https://jsfiddle.net/5fs3dkqj/2/)
    Ответ написан
  • Почему метод fetch не устанавливает headers-запроса?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    На счет нативного fetch, в MDN пример следующий:

    var myHeaders = new Headers();
    
    myHeaders.append('Content-Type', 'text/xml');
    myHeaders.get('Content-Type') // should return 'text/xml'


    Я использую библиотеку isomorphic-fetch.
    Для заголовков есть пара хелперов, выглядит так:

    export function httpGet(url) {
      return fetch(url, {
        headers: buildHeaders(),
      })
      .then(checkStatus)
    }


    Заголовки строю следующим образом:

    function buildHeaders() {
      const authToken = localStorage.getItem('myapp.token')
      return { ...defaultHeaders, Authorization: authToken }
    }


    Изначально, значение у defaultHeaders следующее

    const defaultHeaders = {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    }
    Ответ написан
  • Можно ли использовать Jquery в ReactJs?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Использовать можно. Здесь годная статья о том, как подключать только "кусочки jQuery" в сборку.
    Там же есть и примеры.
    Ответ написан
    Комментировать
  • Актуальны ли Каторовские уроки по NodeJs?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    А какие требования были? Если: видео, русский язык - то лучше курса я не знаю.
    Если же требования: видео/статьи, язык: русский/английский, то думаю да, после изучения первой части курса, лучше поискать боевой пример поновее. Но! Много не потеряете, если посмотрите вторую часть и поделаете на старой версии.
    Ответ написан
    Комментировать
  • Как в связке Redux + React.js кешировать ответы сервера и где?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Как вам уже написал Aves в компоненте перед вызовом экшена проверяйте, если данные уже есть - не делайте запрос. Выглядеть может так:

    loadData() {
      if (state.data.length) {
        // ничего не делай
      } else {
        this.props.callActionAndLoadData()
      }
    }
    Ответ написан
    Комментировать
  • Как реализовать отправку отрендерившегося сообщения (React) чата в другой html-класс?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Использовать flux/redux
    Использовать "события" (хоть custom Event , хоть EventEmitter (после заголовка Глобальная система событий), хоть jquery events)
    Ответ написан
    Комментировать
  • Почему webpack-dev-server ен обновляет страницу?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    в секции с лоадерами:
    {
            test: /\.js$/,
            loaders: ['react-hot', 'babel'],
            //include/exclude и тд
          },


    в секции с плагинами:
    plugins: [
        new WebpackNotifierPlugin, // и другие...
        new webpack.HotModuleReplacementPlugin()
      ],
    Ответ написан
    Комментировать
  • Почему React DevTools не появляется в браузере?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Вы запускаете на неком сервере (mamp, webpack-dev-server и т.д.)? Dev tools не работает, если просто открыт html файл (адрес типа file:// )
    Ответ написан
    1 комментарий
  • Не срабатывает onchange в React Datepicker?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    У вас на onChange функция, которая просто форматирует дату. И все, я даже затрудняюсь сказать, какой от этого будет эффект, но помоему никакой.

    Нужно на onChange прокидывать функцию - создатель действия, например, у нас это будет myAction.

    Следовательно, где-то там в actions будет примерно такой код:

    export function myAction(date) {
      return {
        type: 'CHANGE_DATE',
        data: date
      }
    }


    Затем где-то в редьюсере, должно быть соответствующее условие, например, на case 'CHANGE_DATE' у вас будет устанавливаться в current дата. (специально не пишу код, чтобы немного поработали сами).

    В конце концов, в вашем контейнере, у вас должны быть mapStateToProps и mapDispatchToProps примерно такого вида:

    const mapStateToProps = (state) => ({
      dates: state.dates,
    })
    
    const mapDispatchToProps = (dispatch) => ({
      myAction: (date) => dispatch(myAction(date)),
    })


    Ну и само подключение DayPickerSection будет выглядеть примерно так:

    <DayPickerSection selected={this.props.dates.current}  myAction={this.props.myAction} />


    А в рендере (имеется в виду в рендере DayPickerSection) будет следовательно:

    const DayPickerSection = ({selected, myAction}) => {
        return (
            <div>
                <DatePicker selected={selected}
                            onChange={(date) => myAction(date) }/>
            </div>
        );
    };


    Итого (читать медленно): в onChange компонента DatePicker попадет свойство this.props.myAction "прокинутое" в DayPickerSection. В свою очередь, в DayPickerSection это свойство будет равно функции, которая будет "прокинута" с помощью mapDispatchToProps в this.props контейнера. Контейнер, называйте его как хотите, в нашем случае это родительский элемент, например, который "приконекчен" (c помощью функции connect), и который из-за этого принимает к себе в this.props данные описанные в mapStateToProps и в mapDispatchToProps, которые в свою очередь являются чем то вроде карты. То есть, в конечном счете в this.props.myAction вашего контейнера попадет функция myAction из вашей папки actions. Конечно, для этого ее нужно импортировать в начале файла (import { myAction } from '../actions'.)

    Контейнер может выглядеть так:

    import React, { Component } from 'react'
    import { connect } from 'react-redux'
    import { myAction } from '../actions'
    
    class App extends Component {
      render() {
        const { dates, myAction } = this.props
    
        return (
          <div className='my-app'>
            <DayPickerSection selected={dates.current}  myAction={myAction} />
          </div>
        )
      }
    }
    
    const mapStateToProps = (state) => ({
      dates: state.dates,
    })
    
    const mapDispatchToProps = (dispatch) => ({
      myAction: (date) => dispatch(myAction(date)),
    })
    
    export default connect(mapStateToProps, mapDispatchToProps)(App)
    Ответ написан
    1 комментарий
  • Что с react-datepicker?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Наверное, забыли подключить файл со стилями.

    require('react-datepicker/dist/react-datepicker.css');
    Ответ написан
    7 комментариев
  • Datepicker React/Redux?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Универсальный рецепт для любых сторонних компонентов.

    1. Сторонний компонент, если он хороший, обычно ничего не знает о данных. Он их только отображает. Это то что нам нужно.

    2. Раз компонент ожидает от нас данные, мы должны их в него передать. Например в контейнере СтраницаБилетов, у вас подключается компонент ВыборДаты. Супер, чтобы в ВыборДаты передать данные используются props. Будет примерно так:
    <ВыборДаты текущаяДата={Дата_из_стора/или роута} />

    либо EN вариант:
    <DatePicker current={myDate} />

    3. Далее, у стороннего компонента (опять же, если он хороший) наверняка, есть какой-то onChange обработчик. Предположим, что он называется onDateChange. Следовательно, мы в контейнере подключаем AC (создатель действия, экшен), и передаем его в компонент. Выглядеть будет примерно так:
    <DatePicker onDateChange={this.props.getTickets} />


    либо русский вариант (просто для понимания, вдруг пригодится)
    <ВыборДаты текущая={мояДата} onDateChange={this.props.загрузиБилеты} />


    Причем, функция загрузки новых билетов для этой даты, это не что иное, как экспортируемая функция из папки с вашими экшенами.
    ---

    Приведу пример с кодом, хотя так как я его вытащил с реального проекта, он может вас запутать некоторой сложностью:

    Это мой компонент DayPicker, который внутри себя подключает компонент DatePicker
    class DayPicker extends Component {
      constructor(props) {
        super(props)
        this.state = {
          start: null,
        }
      }
      componentWillMount() {
        const { startTime } = this.props
        this.setState({ start: startTime })
      }
      componentWillReceiveProps(nextProps) {
        const { startTime } = nextProps
        this.setState({ start: startTime })
      }
      render() {
        const { start } = this.state
        const { onDateChange } = this.props
    
        return (
          <section>
            <div className='date-picker'>
              <DatePicker
                className='form-control'
                placeholderText={'Введите дату'}
                selected={moment(start)}
                onChange={ (moment) => onDateChange('startTime', moment) }/>
            </div>
          </section>
        )
      }
    }


    Тем не менее, это компонент. И он должен как-то получать в себя данные. Это происходит в контейнере, кода там много, приведу кусочек:

    // обработчик onDateChange
    onDateChange(dateField, moment) {
      const query = this.props.location.query
      const nextQuery = {
        ...query,
        [dateField]: moment.format('YYYY-MM-DD'),
      }
      // так как у меня все завязано на роутинге, здесь другой вызов ниже,
      // но для простоты примеры, здесь необходимо вызвать экшен, например getTickets(dateField)
    }
    
    // это находится в render функции
    const { startTime } = this.props
    
    <div className='row row-margin'>
      <div className='col-md-12'>
        <DayPicker
          onDateChange={this.onDateChange}
          startTime={startTime}
        />
      </div>
    </div>
    
    // ваш экшен getTickets(dateField) наверняка подключается так
    import { getTickets } from '../../actions/TicketsActions'
    //...
    const mapDispatchToProps = (dispatch) => ({
      getTickets: (dateField) => dispatch(getUsers(dateField)),
    })
    //...
    
    // ваша переменная startTime наверняка приходит из редьюсера / роута, у меня из роута
    const mapStateToProps = (state, ownProps) => ({
      startTime: ownProps.location.query.startTime,
    })
    Ответ написан
    8 комментариев
  • Webpack годится в продакшн?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Добрый день, как уже было сказано выше - задача webpack'a собрать файл (со стилями и js, или только js), который называется в общем случае "бандл". Для этого, бандл нужно собрать (сделать билд) с помощью production конфига для webpack. Продакшен конфиг обычно включает в себя различные "штуки" для оптимизации конечного размера бандл файла, ведь чем меньше - тем лучше.

    Далее бандл вы выкладываете к себе на сервер и отдаете как обычный статичный файл. В таком случае, после изменений кода, вы должны будете:
    а) заново сделать билд (собрать новый бандл)
    б) перезаписать файл на сервере

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

    В dev конфиге, например, часто используют webpack-dev-server, который позволяет пользоваться такой штукой как HMR (hot module replacement), которая в свою очередь позволяет очень комфортно разрабатывать: вы изменяете что-то в файле, и у вас сразу в открытом окне браузера отображаются изменения (без перезагрузки страницы).

    В prod конфиге такое конечно не нужно.

    Пример команды, для того, чтобы сделать prod бандл:
    NODE_ENV=production webpack --progress --config ./webpack.prod.config.js -p


    Главное здесь -p, так как эта опция говорит вебпаку, что необходимо сделать продакшен-сборку. Так же, в этой команде указан специальный конфигурационный файл (webpack.prod.config.js) и переменная NODE_ENV имеет значение 'production'

    Поэтому, точнее будет ответить: с вебпаком можно собрать файл скриптов (и, если настроено, стилей) для продакшена.
    Ответ написан
    Комментировать
  • Как можно переключить страницу исползуя React Router после получения данных?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Можете решить прелоадером. Пока нет данных - рисуете прелоадер (строку "загружаю...", часики, или что еще).
    Ответ написан
    1 комментарий
  • Как перенести часть строки на новую строку в свойстве компонента?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Возможно поможет dangerouslySetInnerHTML

    Пример использования есть в туториале (поиском по странице "rawMarkup")
    Ответ написан
    Комментировать
  • Как заставить реакт рисовать компонент с нуля?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    В reducer'e заводите переменную, например isFetching с состоянием true/false в моменты выполнения асинхронного запроса (true на REQUEST, false на SUCCESS).

    В шаблоне компонента стилизуете этот момент с переменной isFetching как угодно: например показываете прелоадер, который полностью перекрывает предыдущие данные и как-то анимируется. Самый примитивный пример:

    ...
    render()
    ...
      if (this.props.isFetching) {
        <p> Loading...</p>
      } else {
        <p> {ВАШИ_ДАННЫЕ} </p>
      }
    Ответ написан
    7 комментариев
  • Как подключить handlebars в .hbs шаблон?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    А вы используете express? Если да, то вам нужен express-handlebars
    Так же нужно установить в качестве "шаблонизатора" - handlebars.
    app.engine('handlebars', exphbs({defaultLayout: 'main'}));
    app.set('view engine', 'hbs'); //в офф.доке используют расширение handlebars
    Ответ написан
  • Модульность на фронтенде?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    (кратко про себя)
    Все лежит в папках: компонент + стиль. Собирается webpack'ом. Но у меня react-проекты.

    (длинно, но вроде бы по делу)
    Если относительно долго занимаетесь - у вас скорее всего уже выработались части, которые похожи - их выносите. Так же скорее всего у вас есть одинаковая структура (обычно это js/css/images и html, либо как вы пишите компонентами (отдельными папками) внутри котороых html + стили и может js ). Делайте шаблон для будущих проектов, в первую очередь удобным для себя - ведь вам с ним работать, а в нем реализуйте то что умеете по-максимуму (жмите картинки, оптимизируйте js и т.д)

    Плагины, которые используете для Gulp, просто проверьте в блэклисте, а так же можете посмотреть новые версии. Вообще, хорошо если вы знаете все свои плагины, в таком случае - вам и этот пункт можно не выполнять.

    кажется, что не использовал это все на 100%

    Всем так кажется, поэтому когда не хочется заниматься работой, идем в гугл и смотрим опен-сорс проекты других людей: gulp, wepback, затем если нашли что-то любопытное идем в npm/github читаем доку. Пытаемся применить в работе.

    Что имеем в итоге?
    1) если все работает и вас устраивает (скорость сборки, удобство проверки в разных браузерах ...) - "работу работать";
    2) если есть время и желание - гуглите опен-сорс решения, читайте твиттер интересных людей / новостную подписку;
    3) если хочется услышать мнение коллег, но при этом коллег рядом нет - пишите статью на хабр. Просто статья: я использую такие-то плагины в своем "шаблоне" - вряд ли получит лестные отзывы, но возможно кто-то напишет: вот в этом месте у вас плохо, сделайте иначе. Возможно, вы придумаете, как написать статью интересно - тогда честь и хвала. И критика. А обоснованная критика всегда хорошо.

    P.S. если используете Jade и следуете BEM-методологии, то я бы порекомендовал посмотреть на этот проект
    Ответ написан
    Комментировать
  • Почему не всегда срабатывает e.target.dataset?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    У вас клик на ссылке, а data-* на элементе <i>
    Когда кликаете по ссылке - e.target = ссылка и у нее нет data-*
    Когда в клике попадаете на i = все хорошо.
    Ответ написан
    3 комментария
  • Как правильно организовать авторизацию веб-сервиса на Express?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Как на счет jwt ?

    За! Удобно, нравится.

    Вкратце - шлете стандартные регистрационные данные, сервер выдает вам токен. Далее токен добавляете в заголовки запросов.

    В прицнипе, в гугле инфы хватает по вопросу. Сам сайт посмотрите (jwt.io), оттуда ссылка на пакет для node

    Скопирую сюда куски кода из своего api сервера, но скажу сразу - в node я не очень силен, если будут замечания от знающих - только рад. Так же angular кода не приведу, потому что клиентский код у меня на react.

    Здесь в файле используются callbacks, возможно есть варианты получше. В других роутах приложения я где-то через async поигрался, где-то через promise.

    Мой роутер auth

    const express = require('express')
    const router = express.Router()
    const User = require('../models/user')
    const v4 = require('node-uuid').v4
    const jwt = require('jsonwebtoken')
    
    router.post('/signup', (req, res, next) => {
    
      req.check('email', 'Please enter a valid email').len(1).isEmail()
      req.check('password', 'Please enter a password with a length between 4 and 34 digits').len(4, 34)
    
      const errors = req.validationErrors()
    
      if (errors) {
        return res.status(400).json({ errors })
      } else {
        User.hashPassword(req.body.password, (err, passwordHash) => {
          if (err) {
            return res.status(400).json({ error: err.message })
          }
    
          const user = new User({
            name: req.body.name,
            nickname: req.body.nickname,
            email: req.body.email,
            password: req.body.password,
          })
    
          user.passwordHash = passwordHash
          user.save((err, item) => {
            if (err) {
              return res.status(400).json({ error: err.message })
            }
            const payload = {
              _id: item._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 })
            })
          })
        })
      }
    })
    
    router.post('/signin', (req, res, next) => {
    
      req.check('email', 'Please enter a valid email').len(1).isEmail()
      req.check('password', 'Please enter a password with a length between 4 and 34 digits').len(4, 34)
    
      const errors = req.validationErrors()
      const password = req.body.password
    
      if (errors) {
        return res.status(400).json({ errors })
      } else {
        User.findOne({ email: req.body.email }, (err, user) => {
          if (err) {
            return res.status(400).json({ error: err.message })
          }
          if (!user) {
            return res.status(400).json({ error: 'User not found' })
          }
          User.comparePasswordAndHash(password, user.passwordHash, (err, areEqual) => {
            if (err) {
              return res.status(400).json({ error: err.message })
            }
            if (!areEqual) {
              return res.status(400).json({ error: 'Wrong password' })
            }
            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;


    Модель user

    const mongoose = require('mongoose')
    const bcrypt = require('bcrypt')
    const Schema = mongoose.Schema
    let bcrypt_cost = 12
    
    const userSchema = new Schema({
      name: { type: String, required: true },
      email: { type: String, required: true },
      passwordHash: String,
    })
    
    userSchema.statics.hashPassword = (passwordRaw, cb) => {
      if (process.env.NODE_ENV === 'test') {
        bcrypt_cost = 1
      }
      bcrypt.hash(passwordRaw, bcrypt_cost, cb)
    }
    
    userSchema.statics.comparePasswordAndHash = (password, passwordHash, cb) => {
      bcrypt.compare(password, passwordHash, cb)
    }
    
    const User = mongoose.model('User', userSchema)
    
    module.exports = User


    В коде роутера есть AUTHO_CLIENT - так как я изначально брал реализацию от AUTH0, кстати, может понравится? У них удобно все... Но для себя в целях обучения переписал на свою. (какая-то ссылка у них про angular cookies vs token)

    Мне нравится, что некоторую информацию я могу взять прямо из токена (например, уровень доступа - в моем примере - poll)
    Ответ написан
  • Как передать параметры в метод?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Добрый день, чтобы ваш прогресс был более быстрым и полезным, нужно подтягивать знания по нативному js.

    Во-первых - onClick={this.updateClick('params')} - что вы ожидаете от такой записи? Давайте поразмышляем: в onClick вы должны передать функцию-обработчик, а вы передаете РЕЗУЛЬТАТ функции this.updateClick так как вызываете ее с ( )

    Во-вторых, как вам правильно подсказали, можно использовать .bind для передачи параметров.
    Для этого, сначала идем в MDN, читаем. Пытаемся передать функцию с параметром в качестве домашнего задания. Подсказка: .bind ставит ваши параметры в начало. То есть все аргументы сдвигаются (в том числе и "e").

    P.S. Если не получится, то можете посмотреть ответ по ссылке.

    P.P.S. Очень важно понять, что есть результат работы функции. То есть, в вашем вопросе, вы передавали в onClick - e.preventDefault(), причем именно это, а не "функцию, которая по клику бы срабатывала и происходил бы e.preventDefault()). В случае с bind, вы передаете результат работы функции bind, который является функцией, другими словами - "все корректно, в onClick передается функция".
    Ответ написан
    Комментировать