Ответы пользователя по тегу React
  • Как отрендерить JSON в React.js?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Всегда когда вам приходит какая-то структура, нужно пробегать ее полностью по каждому элементу, чтобы сгенерировать JSX-элемент. Для массивов обычно используют .map метод, для объектов - например Object.keys (так же подойдут конечно, и цикл forEach для массива, и цикл for in для объекта и т.д.)

    Так как у вас в JSON приходит не массив объектов, а объект объектов то вам нужно пробегать с помощью Object.keys, например, и потом еще применить map к получившемуся массиву ключей. Пример получившегося массива:

    6b13d2ad4f6241f4bb3c378385646082.jpg

    Применяем map:

    const template = Object.keys(data.books).map(item => <span key={data.books[item].id}>{data.books[item].author} - {data.books[item].name}</span>)


    В данном случае в переменной template окажутся 2 JSX-элемента (тэги span с данными). Вам скорее всего нужно, чтобы это было tr. Поменять не трудно)

    А json-loader вам нужен для того, чтобы webpack мог корректно импортировать такие файлы, так как пока json-лоадера нет, вебпак не знает как обрабатывать файлы с расширением .json. По такому же принципу вы подключаете css-loader, например, чтобы webpack мог "импортить цсски"
    Ответ написан
    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 комментариев
  • Как поставить 2 redux thunk на разные редюсеры?

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

    Redux-thunk - это всего лишь вспомогательная функция для ваших экшен-крейтеров (или просто экшенов, как удобнее). Если посмотреть на исходный код, то можно описать его так: если AC (экшен, экшен крейтор, создатель действия...) возвращает простой объект - прокидывай его дальше, если возвращает функцию, то "прокинь" в эту функцию dispatch и getState функции в качестве аргументов. Например, это дает нам возможность работать с асинхронными запросами. Так как, чтобы достучаться до reducer'a мы же должны использовать функцию dispatch. А если бы не было redux-thunk, то откуда бы она взялась у нас в коде экшена: импорта в файл нет, нигде не описано, что const dispatch = ...?

    Приведу пример:

    export function logout {
      return {
        type: LOGOUT_SUCCESS,
      }
    }


    AC возвращает простой объект, который (если нет других middleware влияющих на работу) - сразу попадает в reducer.

    export function login(data) {
      return dispatch => {
        dispatch({ type: LOGIN_REQUEST })
    
        return request.post(`${API_ROOT_V1}/auth/signin`)
          .send(data)
          .then(res => {
            if (!res.ok) {
              dispatch({ type: LOGIN_FAILURE })
              dispatch(showNotification({
                status: 'err',
                text: 'something going wrong',
              }))
    
            } else {
              dispatch({
                type: LOGIN_SUCCESS,
                data: res.body.data,
              })
              localStorage.setItem('cks_token', res.body.data)
              dispatch(push('/'))
            }
          }, err => {
            dispatch({ type: LOGIN_FAILURE })
            dispatch(showNotification({
              status: 'err',
              text: err.body && err.body.error || err.message,
            }))
          })
      }
    }


    Особое внимание на return dispatch => { - это значит, что АС возвращает функцию, а следовательно в этой функции из-за redux-thunk доступен dispatch. Если вводит в заблуждение ES2015 синтаксис, то это можно было бы написать как return function(dispatch) { ...

    P.S. Опишите подробнее, что вы имеете в виду под "А как разделить логику обработки?"
    Ответ написан
    Комментировать
  • React + Redux, как начать правильно?

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

    Не торопиться, если хочется вдумчиво разобраться =)

    Первая проблема, как принять данные?

    Использовать react lifecycle hooks: в момент componentDidMount (или в componentWillMount) вызвать actionCreator (AC = создатель действия) -> в AC вы генерируете событие с типом REQUEST (которое ловит ваш редьюсер), а затем производите "ajax запрос", скажем так. В случае удачного результата - генерируете событие SUCCESS и ваш редьюсер устанавливает данные. Далее, ваш компонент формы, видит, что есть новые "props" (свойства, которые изменились в редьюсере, о которых компонент узнал, так как он приконекчен с помощью функции "connect" из библиотеки react-redux) и запускается перерендер. Вуаля, после такой кучи действий ваша форма "приняла данные".

    Конечно, если просто сделать нативный xhr запрос, кода будет гораздо меньше и все в одном файле, но это уже нужно исходить из ваших пожеланий. Если вам нужно, чтобы данные были "прогнаны" через редьюсер и оказались в store, значит "много действий". Если не нужно - просто обычный xhr запрос, либо если угодно $.ajax и установка данных в state компонента.

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


    Если используется redux, то цепочка следующая: на onChange селекта вызывается обработчик, то есть функция в вашем компоненте, которая вызывает AC (создатель действия, который располагается в папке actions). Далее уходит запрос на сервер. Затем с сервера приходит успешный ответ - вы генерируете событие, типа "DATA_RESPONSE_SUCCESS" и ваш редьюсер его "ловит". Итак, вы оказываетесь в третьем файле - в файле с редьюсером. Там вы манипулируете данными, так как вместе с типом события, вы так же должны были передать из action'а и данные, которые пришли с сервера. После того, как вы установите новые данные в редьюсере - начинается магия (которая заключается в том, что ваш компонент слушает изменения в объекте стора, с помощью функции connect). Ваш компонент перерендеривается и в инпуте оказываются нужные данные, так как инпут в качестве value использует, например: this.props.myNewValueFromServerAfterSelectManipulation (имя переменной, конечно, шуточное).

    Итого: вы потрогали файл компонента, файл из папки actions и файл из папки reducers.

    Супер итого:
    1) вам нужно понять, как сделать форму без redux. Как в ней с помощью this.state и методов жизненого цикла устанавливать и изменять данные в зависимости от того, что выбрал/ввел пользователь. Сделать это не сложно, если начать с туториала на официальной странице, либо заглянуть в РУ туториал здесь.

    2) вам нужно понять, почему в actions располагаются асинхронные запросы, и как при этом работает redux-thunk (а так же middlewares в общем). Почему в reducers производится только изменение данных. И самое главное, почему компонент при этом перерисовывается. Опять же, ссылки на официальные руководства уже дали. Русский перевод там не закончен, поэтому, хоть и устаревают версии библиотек, этот туториал по редуксу до сих пор актуален.

    P.S. в туториалах есть примеры с кодом
    Ответ написан
    3 комментария
  • Нужен ли React без Flux?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Я думаю, если React использовать без Flux/Redux и подобных, то ваши заказы - это какие-то плагины.
    Исходя из своего опыта, могу сказать, что задачи на разработку плагинов под реакт - редкость.

    В любом случае, "flux" вы поймете и обкатаете еще раньше, чем сможете сказать: я 100% знаю реакт и могу его использовать для сложных задач.
    Ответ написан
    Комментировать
  • Накидайте больших примеров react & redux приложений?

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

    https://github.com/WebbyLab/itsquiz-wall
    www.robinwieruch.de/the-soundcloud-client-in-react... (git)

    в остальном: разрастается папка с экшенами и редьюсерами, появляется папка с какими-то хелперами (функциями, которые используются в разных компонентах, например форматирование даты в ЧЧ:ММ:СС из секунд...), появляется больше зависимостей (как и на всех других проектах). Приходится писать больше тестов и сборка/пересборка происходит медленнее.
    Ответ написан
  • Как работает серверный рендеринг React.js с объектами браузера в компонентах?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Могу быть не прав, но я так понял, что все объекты браузера мокаются (как будто в тестах, то есть делается например "ложный localStorage", ведь это по сути объект у котрого есть getter и setter. Для тестов я использовал - mock-local-storage). "Мокнуть" можно и самому, для этого писать нужно в global, так как в node окружении глобальный объект не "window". Подробнее можно посмотреть в коде библиотеки.

    Опять таки, давайте продолжим про localStorage, так как удобнее на конкретном примере. Почему мок нормально подойдет, как мне кажется? Потому что это будет как будто к вам на сайт зашел пользователь, у которого ничего в LS по вашему сайту не сохранено. Главное чтобы сработало: localStorage.setItem / getItem, и так как вы эти функции написали и объявили глобально - все получится (естественно, в коде у вас должно быть localStorage без window, либо нужно будет делать мок не просто в global, а в global.window - иными словами мы создаем в глобальом объекте объект window).

    P.S. на практике я не делал серьезного SS рендеринга, поэтому буду рад услышать, мнение тех, кто уже разобрался. В зарубежных источниках есть информация (например), и даже неплохо разобраны вещи с асинхронными запросами, но тем не менее, хотелось бы увидеть еще.
    Ответ написан
    2 комментария
  • 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'

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

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Возможно, chrome не хочет открывать по пути file:// файл с непонятным типом.
    Как вариант, если взять код отсюда, то можете просто запустить локально сервер командой node server.js и открыть в хроме адрес localhost:3000 - будет работать.
    Ответ написан
  • Как можно переключить страницу исползуя React Router после получения данных?

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

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Не могут.
    Сможете спокойно внедрять react-код для необходимых "кусков" (зон с данными, или не знаю как еще назвать, областей?) на странице.
    Ответ написан
    Комментировать
  • React, Router и Аутентификация. Как лучше реализовать?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Задачу можно разделить на 2 части:

    1) получение токена (идентификатора сессии или что еще нужно) от сервиса.
    2) onEnter хук в роутере

    Примерный код:
    export default function configRoutes(store) {
      function _ensureAuthenticated(nextState, replace, callback) {
        const { dispatch } = store 
        const { session } = store.getState()
        const { currentUser } = session
    
        if (!currentUser && localStorage.getItem('token')) {
          dispatch(getCurrentAccount())
        } else if (!localStorage.getItem('token')) {
          replace('/signin')
        }
    
        callback()
      }
    
      return (
        <Route component={App}>
          <Route path='/signin' component={SigninContainer} />
          <Route path='/signup' component={SignupContainer} />
    
          <Route path='/' component={AuthenticatedContainer} onEnter={_ensureAuthenticated}>
            <Route component={ReportContainer}>
              <Route path='/activities' component={ActivitiesContainer} />
              <Route path='/activities/:id' component={CaptionsContainer} />
            </Route>
          </Route>
    
        </Route>
      )
    }


    Более подробно можно посмотреть в источнике. ConfigRoutes функция вызывается здесь.
    Ответ написан
    Комментировать
  • ReactJS + Electron. В чем ошибка?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Судя по ошибке, babel не может преобразавать "react-компонент" и для него "<" (22:2 - первый символ в строке <Provider... , а так же если вы убирали , то первый символ в<App...) - это не валидный код.

    Добавьте в файлик с настройками пресет "react", (или создайте с подобным содержимым)

    .babelrc
    {
      "presets": ["es2015", "stage-0", "react"],
    }
    Ответ написан
    Комментировать
  • React.js. Сайт целиком на реакте или применять реакт модульно?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    В свое время "подсел" на реакт, потому что он позволял переделывать "кусочки" продукта, а не все целиком. Это нравилось и заказчикам (меньше рисков, быстрее) и мне.

    У вас получается, главный плюс если все будет на react - 100% SPA, которое потом можно показать как проект в портфолио в данной тематике. Особенно стоит задуматься о веб-приложении полностью на реакте, если интересует подобная работа в будущем.

    Не малый плюс, что в случае с React вам будет нужно написать API и потом это же API можно будет использовать в мобильном приложении(?)

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

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

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

    Пример использования есть в туториале (поиском по странице "rawMarkup")
    Ответ написан
    Комментировать
  • Пробрасывание функций из actions через дерево компонентов vs connect функций в компоненте?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Добрый день.

    Везде использую connect. Почему? Потому что "пробрасывание через дерево" - не видел в оригинальной документации, а так же вообще не видел. Имеется ввиду использование context?

    Мне нравится connect, все устраивает.

    В записи для connect (а точнее для mapDispatchToProps) использую или не использую bindActionCreators:
    const mapDispatchToProps = (dispatch) => ({
      pollActions: bindActionCreators(PollActions, dispatch),
    })


    const mapDispatchToProps = (dispatch) => ({
      getUsers: (params) => dispatch(getUsers(params)),
      dispatch,
    })


    P.S. покажите пример с пробрасыванием функций из actions через дерево компонентов, и если уже что-то интересное для себя в этом подходе выделили - напишите. Будем смотреть вместе.
    Ответ написан
  • Как в Reac Router программно поменять путь?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Путаница в вопросе:
    1) как программно поменять путь?
    2) как с помощью RR показать второй компонент через 10 сек?

    То есть, вам нужно изменять роут, например через 10 секунд? (таким образом компонент, принадлежащий этому роуту будет показан).

    Программно изменить роут можно с помощью push, replace (там же). Там же есть примеры, один из них.
    Ответ написан
  • Как заставить реакт рисовать компонент с нуля?

    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 комментариев