• Что лучше выбрать для React.js приложения: Flux, Reflux, Redux?

    По десять раз за день такие вопросы)

    Redux самый перспективный и крутой из всех. Если не знаете ничего - начните с этого: https://egghead.io/series/getting-started-with-redux (от создателя редакса).

    Плюсы редакса в том, что он построен в функциональных парадигмах, как и реакт. При этом отлично работает с react-native. API очень маленькое, по факту почти ни один из моих компонентов ничего не знает о редаксе - просто принимает на вход нужные props.
    Ответ написан
    4 комментария
  • Как правильно разбивать код на компоненты?

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

    Компонент для вывода статистики так и просится:

    <MyStatistic items={[ { titile: 'abc', message:  'cde' } ]}
    Ответ написан
    2 комментария
  • Какие есть визуальные инструменты для создания архитектуры БД?

    Для symfonty + doctrine я использовал https://www.skipper18.com/en/features
    Триальная версия мне понравилась (сразу генерирует код сущностей), но вот платить 500$ за лицензию пока жаба душит.
    Ответ написан
    Комментировать
  • Как в React сделать задержку onMouseEnter?

    Я только рекомендую сделать эту функцию через debounce (или аналоги):

    constructor(props) {
        super(props);
        const finalMouseDelay = props.mouseDelay || 300;
        
        this.handleMouseLeave = this.handleMouseLeave.bind(this);
        this.handleMouseEnter = this.handleMouseEnter.bind(this);
        this.toggleHover = _.debounce(this.toggleHover.bind(this), finalMouseDelay);
        
        this.state = { hover: false };
      }
    
      handleMouseLeave() {
        this.toggleHover(false);
      }
      
      handleMouseEnter() {
        this.toggleHover(true);
      }
    
      toggleHover(to) {
        this.setState({ hover: to });
      }


    Рабочий пример тут - jsfiddle.net/yuvuyrhv/24

    Иначе у Вас будут дикие лаги, если пользователь навел мышку\убрал мышку\снова навел ... с интервалом быстрее, чем заданный вами
    Ответ написан
    2 комментария
  • Как сохранить тип объекта в JavaScript после JSON преобразований?

    Такого механизма нету. Можно использовать нечто подобное (рабочий пример https://jsfiddle.net/83bun7xt/ ):
    function serialize(entity) {
        return JSON.stringify({ type: entity.constructor.name, data: entity });
    }
    
    function deserialize(str, types) {
        var parsed = JSON.parse(str);
        return types[parsed.type] ? new types[parsed.type](parsed.data) : {};
    }
    
    function User(data) {
      this.id = data.id;
      this.name = data.name;
    }
    
    var user = new User({ id: 1, name: 'Nik' });
    console.log('User ->', user, ' instanceOf User ===', user instanceof User);
    // User -> User {id: 1, name: "Nik"}  instanceOf User === true
    
    var userJSON = serialize(user);
    console.log('Serialized user ->', userJSON);
    // Serialized user -> {"type":"User","data":{"id":1,"name":"Nik"}}
    
    var restoredUser = deserialize(userJSON, { 'User': User });
    console.log('Deserialized user ->', restoredUser, ' instanceOf User ===', restoredUser instanceof User);
    // Deserialized user -> User {id: 1, name: "Nik"}  instanceOf User === true
    
    //
    Ответ написан
    1 комментарий
  • Что такое Falcor?

    Нет:

    Falcor is middleware. It is not a replacement for your application server, database, or MVC framework. Instead Falcor can be used to optimize communication between the layers of a new or existing application.
    Ответ написан
    4 комментария
  • Как использовать миксины в React.js ES6/7?

    Да, миксинов теперь нету. Используйте декоратры -> https://github.com/brigand/react-mixin
    Ответ написан
    Комментировать
  • React и Flux: как получить данные при загрузке страницы?

    Store.fetchData() возвращает Promise. Вам нужно дождаться пока придет ответ и потом уже рендерить Ваш компонент.

    Store.fetchData().then( ... )

    Или подписываться на Store каким-либо образом (зависит от выбранной реализации flux)
    Store.on('DATA_LOADED', (data) => this.setState({ data }); //как пример
    Ответ написан
    3 комментария
  • Нужно ли при POST запросе делать reducer в redux'е?

    Редьюсер нужен когда Вы хотите хранить состояние. Это может быть, например, статус запроса - упех/в процессе/ошибка. И исходя из этого сделать кнопку "отправить" не активной, показать индикатор загрузки и т.д.
    Ответ написан
    1 комментарий
  • Какой изоморфный роутер выбрать для ReactJS?

    По мне так react-router - лучшее решение. С гет запросами проблем ни каких, потому что он не обрабатывает гет запросы)

    Все библиотеки новые, эта ниша только развивается, так что решений "проверенных временем" сейчас нету.

    UPD1:
    copal: вот только что тыкал как псих - все работает. Если только загрузить страницу и сразу кликнуть - может возникнуть ситуация, что роутер еще не включился и тогда ссылка отработает как простая ссылка - с сервера загрузится новая страница.
    Ответ написан
    2 комментария
  • Как в React не показывать кусок компонента?

    Если условие небольшое, то можно не выносить в отдельную функцию (как предложил OneFive )

    renderAlert() {
      return (
        <Alert bsStyle="warning" onDismiss={this.hideAlertByClick}>
            <strong>Внимание</strong> - эксперементальный проект, в стадии разработки. Заявленные функции будут подключаться по мере его развития.
        </Alert>
      );
    }
    
    render() {
      const { showAlert } = this.state;
      return (
        <div>
            <b> Some text </b>
            { showAlert && this.renderAlert() }
        </div>
      );
    }
    Ответ написан
    Комментировать
  • Как правильно получать и обрабатывать данные из форм в React.js?

    Воспользуйтесь, например, этой библиотекой -> https://github.com/erikras/redux-form .В ней есть поддержка динамических форм. Все поля будут по факту в одном объекте и относительно этого Вы можете применять свою логику рендера\расчетов.
    Ответ написан
    Комментировать
  • Как выполнить экшен после выполнения другого екшена?

    Единственный способ нативно в редаксе выполнять экшены в определенном порядке - это thunk
    functiob action1Creator() {
      return (dispatch, getState) => {
        fetch(...)
          .then(JSON.parse)
          .then((response) => dispatch(action2Creator(response)))
        ;
      };
    }
    
    function action2Creator(response) {
      return { type: 'SOME', data: response.data[12] };
    }
    Ответ написан
    Комментировать
  • Объясните простыми словами как работает 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 комментария
  • React + Redux. Правильное использование QueryString и componentWillReceiveProps?

    В такой ситуации компонент следует подключить к redux'у через connect. Тогда у вас будет возможность смотреть, что уже загружено, а что нет.

    Если у вас какая-то сложная логика - я бы предложил сделать флаг isLoaded и передавать его как props в компонент. Затем, в методе componentWillReceiveProps вы можете проверить, если этот флаг сброшен - вызвать функцию загрузки.

    Но конкретно в этом случае лучше поступить так, как сделано в официальном примере real-world-example: проверять изменился-ли номер страницы и только в этом случае диспатчить действия.

    UPD1. Пример кода:
    // MyComponent
    import shallowequal from 'shallowequal';
    
    export default class MyComponent {
      static propTypes = {
        items: PropTypes.array.isRequired,
        filtres: PropTypes.object.isRequired, // <-- тут можно собрать все фильтры
      };
    
      componentWillReceiveProps(nextProps) {
        if(!shallowequal(this.props.filtres, nextProps.filtres)) {
            loadData(filtres);
        };
      }
    }


    // MyComponentContainer
    import MyComponent from './MyComponent';
    
    function mapState(state, ownProps) {
      return {
        items: applyFiltres(state.someReducer.someItems),
        filtres: ownProps.location.query, // <--- как пример
      };
    }
    
    export default connect(mapState)(MyComponent);


    В этом случае наш компонент (MyComponent) независим от роутера и от редакса. При этом метод componentWillReceiveProps достаточно простой.
    Ответ написан
    6 комментариев
  • Где и как нужно сделать AJAX запрос в redux?

    Для работы с promise Вам нужно использовать дополнительный middleware - promise middleware. Существует несколько реализаций - можете выбрать любой понравившийся. Я использую этот и относительно него буду далее писать.

    Как я понял, запрос пишется в action creator при помощи промиса


    Да, все так. В экшен-креаторе делаем запрос, возвращаем экшен с промисом. Далее promiseMiddleware подхватывает его и вместо одного экшена диспатчит 3 - действие началось, действие завершилось успешно, действие завершилось с ошибкой. Все это происходит в зависимости от того в какое состояние попал вышеупомянутый promise. Другими словами - "действие началось" диспатчится сразу; если запрос успешен - диспатчится "действие завершилось успешно".
    Соответственно в вашем редьюсере Вы подписываетесь на это три действия. Пример:

    // action creator
    import fetch from 'isomorphic-fetch';
    
    export default function getBook() {
      // Для запросов чаще всего используется  isomorphic-fetch, который возвращает promise
      const promise = fetch(url, options);
      return {
        types:  [ 'GET_BOOK-BEGIN', 'GET_BOOK-SUCCESS', 'GET_BOOK-FAILURE' ],
        promise,
      };
    }


    // reducer
    import {GET_BOOK} from '../actions/books';
    
    const booksReducer = (state = {}, action) => {
      switch (action.type) {
        case 'GET_BOOK-SUCCESS':
          // При успешном завершении у нас установлено свойство result с ответом сервера
          return action.result;
        case 'GET_BOOK-FAILURE':
          // При неудачном завершении у нас установлено свойство error с описанием ошибки
          doSomethingWithError(action.error); // <--- просто для примера
        default:
          return state;
      }
    };
    
    export default booksReducer;


    UPD1 К слову: у редакса есть отличный пример, где все это можно в живую посмотреть - https://github.com/rackt/redux/tree/master/example...
    Ответ написан
    6 комментариев
  • Как на React сделать вертикальное меню с выделением элемента меню?

    Надо, чтобы при нажатии на элемент меню он выделялся другим цветом. Это все получилось сделать.

    Странно, что одно работает, а другое - нет. Механизм ведь один и тот-же. Простейшая реализация предполагает сохранение выбранного элемента меню в state:

    class MyMenu extends React.Component {
    
      constructor(props) {
        super(props);
        this.handleItemClick = this.handleItemClick.bind(this);
        this.state = { selectedItem: null };
      }
    
      handleItemClick(selectedItem) {
        console.log('handleItemClick', selectedItem);
        this.setState({ selectedItem });
      }
    
      render() {
        const { items } = this.props;
        const { selectedItem } = this.state;
        const { handleItemClick } = this;
    
        return (
          <ul>
            { items.map( (item, key) => (
                <MenuItem
                    key={key}
                    item={item}
                    isActive={item === selectedItem}
                    onClick={handleItemClick}
                 />
            )) }
          </ul>
        );
      }
    }


    И, соответственно, в самом компоненте MenuItem проверяем - если флаг isActive === true, то добавляем класс или еще чего:
    Рабочий пример
    Ответ написан
    1 комментарий