понедельник, 31 июля 2017 г.

Асинхронные операции с использованием redux-saga


Несколько дней назад мой коллега заговорил об управлении асинхронными операциями. Он использовал несколько инструментов для расширения возможностей redux. Наслушавшись его я поехал домой реально усташим от JavaScript.

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

Я провел последние два года, работая над проектами Angular и наслаждаясь современным уровнем развития Модели-Вида-Контроллера (MVC). И я должен сказать, что даже если кривая обучения была с проблеммами пришедшими из Backbone.js - обучение Angular действительно оправдалось. Я получил лучшую работу, и у меня также появилась возможность принять участие в интересных проектах. Я многому научился у сообщества поддерживающего Angular.

Это были действительно классные дни, но, ну, «Усталость должна продолжаться» (копирайт), и двигаюсь с модой: React, Redux и Sagas. Несколько лет назад я наткнулся на статью под названием «Сглаживание цепочек промисов» Томаса Бурлесона. Я многое узнал чтения прочитав это. Даже через два года я все еще вспоминаю множество этих идей.

В эти дни я перешел на React, и я нашел мощь Redux и использовал саги для управления асинхронными операциями. Поэтому я пишу это, чтобы позаимствовать пост Томаса и создать аналогичный подход, используя redux-saga. Надеюсь здесь вселенная будет благосклонна и поможет некоторым людям понять, как работают эти важные технологии. Отступление: я буду работать с одним и тем же сценарием и расширять его, надеюсь (если повезет), чтобы создать обсуждение обоих подходов. Я предполагаю, что у читателя есть некоторое базовое понимание Promise, React, Redux и ... JavaScript.

Сначала первичные вещи.

Согласно Yassine Elouafi, создателю redux-saga. Redux-saga - это библиотека, которая направлена на упрощение и улучшение сайд-эффектов (например, асинхронные вещи, такие как извлечение данных и нечистые вещи, такие как доступ к кешу браузера) в приложениях React / Redux.

В основном это вспомогательная библиотека, которая позволяет нам организовывать все асинхронные и распределенные операции генераторов функций на основе Sagas и ES6. Если вы хотите больше узнать о самом шаблоне саги, у Caitie McCaffrey отлично поработано в этом видео и больше рассказано о генераторах функций. Взгляните на это бесплатное видео Egghead (по крайней мере, это было бесплатно, когда я опубликовал эту статью).

Вариант панели управления полетами.

Томас установил случай, который мы собираемся воссоздать. Финальный код здесь, и демо здесь. Сценарий выглядит примерно так:

Как мы видим последовательность вызовов API следующая: getDeparture → getFlight →getForecast (Получить отправляемого→Получить рейс→Получить прогноз), примерно так выглядит наш сервисный класс для этого:

class TravelServiceApi {
 static getUser() {
   return new Promise((resolve) => {
     setTimeout(() => {
       resolve({
            email : "somemockemail@email.com",
            repository: "http://github.com/username"
       });
     }, 3000);
   });
 }
 static getDeparture(user) {
  return new Promise((resolve) => {
   setTimeout(() => {
    resolve({
      userID : user.email,
      flightID : “AR1973”,
      date : “10/27/2016 16:00PM”
     });
    }, 2500);
   });
 }
 static getForecast(date) {
  return new Promise((resolve) => {
      setTimeout(() => {
        resolve({
            date: date,
            forecast: "rain"
        });
      }, 2000);
   });
  }
}

Это простой API с некоторой демо-информацией, которая позволит нам создать сценарий. Сначала мы должны иметь пользователя. Затем с этой информацией мы получим вылет, полет и прогноз, чтобы мы могли создать несколько уродливых панелей приборных панелей, которые выглядят следующим образом:

Компоненты React можно найти здесь. Они представляют собой три разных компонента с представлением в магазине redux, которое дается тремя редюсерами, которые выглядят следующим образом:

const dashboard = (state = {}, action) => {
 switch(action.type) {
  case ‘FETCH_DASHBOARD_SUCCESS’:
  return Object.assign({}, state, action.payload);
  default :
  return state;
 }
};

Мы используем другой редюсер для каждой панели с тремя различными сценариями, которые дают компоненту доступ к части пользователя с помощью функции redux-а StateToProps:

const mapStateToProps =(state) => ({
 user : state.user,
 dashboard : state.dashboard
});

После того, как все настроено (да, я знаю, что я не объяснял много чего, но я хочу сосредоточиться только на сагах ...), мы готовы играть!

Покажите мне Саги

Уильям Деминг сказал однажды: Если вы не можете описать, что вы делаете как процесс, то вы не знаете, что делаете. Хорошо, давайте создадим пошаговый процесс работы с Redux Saga.

1. Регистрация Саги

Я буду использовать свое собственное слово, чтобы описать какие методы API открытые. Если вам нужна дополнительная техническая информация, не стесняйтесь обращаться к документации здесь.

Сначала нам нужно создать наш генератор саги и зарегистрировать их:

function* rootSaga() {
  yield[
    fork(loadUser),
    takeLatest('LOAD_DASHBOARD', loadDashboardSequenced)
  ];
}

Saga Redux выделяет несколько методов под названием Effects, мы собираемся определить несколько из них:


Fork - выполняет неблокирующую операцию над переданной функцией.
Take - пауза до получения экшена.
Race - запускает эффекты одновременно, а затем отменяет их всех кроме той, которая первая завершится.
Call - выполняет функцию. Если он вернет обещание, приостанавливает сагу до тех пор, пока обещание не вызовет resolve.
Put — диспатчит экшн.
Select - запуск функции выбора для получения данных из state.
takeLatest - означает, что мы будем вызывать несколько call, а затем возвращать только результаты последнего вызова call.
takeEvery - вернет результаты для каждого вызванного call.

Мы просто зарегистрировали две разные саги. Мы собираемся определить их позже. До сих пор мы использовали одну сагу для пользователя, использующую fork и другую использующую takeLatest, которая будет ждать выполнения действия LOAD_DASHBOARD. Дополнительная информация на шаге 3.

2. Инжектирование Saga Middleware в хранилище Redux.

Когда мы определяем хранилище Redux и инициализируем его, большую часть времени он будет выглядеть так:

const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, [], compose(
      applyMiddleware(sagaMiddleware)  
);

sagaMiddleware.run(rootSaga); /* inject our sagas into the middleware*/

3. Создание саги.

Во-первых, мы собираемся определить последовательность саги loadUser:

function* loadUser() {
  try {
   //1st step
    const user = yield call(getUser);
   //2nd step
    yield put({type: 'FETCH_USER_SUCCESS', payload: user});
  } catch(error) {
    yield put({type: 'FETCH_FAILED', error});
  }
}

Мы можем прочитать это так:

Во-первых вызываем функцию, называемую getUser, и присваиваем результат в const user. Далее отправляем экшен под названием FETCH_USER_SUCCESS и передаем значение user, которое будет потребляться хранилищем.

Если что-то пойдет не так, отправьте экшн под названием FETCH_FAILED.

Как вы можете видеть, действительно здорово, что мы можем положить результат операции yield в переменную.

Теперь мы собираемся создать последовательность в саге:

function* loadDashboardSequenced() {
 try {
  
  yield take(‘FETCH_USER_SUCCESS’);
  const user = yield select(state => state.user);
  
  const departure = yield call(loadDeparture, user);
  const flight = yield call(loadFlight, departure.flightID);
  const forecast = yield call(loadForecast, departure.date);
  yield put({type: ‘FETCH_DASHBOARD_SUCCESS’, payload: {forecast,  flight, departure} });
  } catch(error) {
    yield put({type: ‘FETCH_FAILED’, error: error.message});
  }
}

Мы можем прочитать сагу следующим образом: Ожидаем, пока прийдет экшн FETCH_USER_SUCCESS. Выполнение саги в основном будет приостановлено до тех пор, пока триггер этого события не вызовет его. Для этого мы используем take - эффект.

Мы берем значение из хранилища. Select - эффект получает функцию, которая имеет доступ к хранилищу. Мы присваиваем информацию о юзере в константу const user.

Мы выполняем асинхронную операцию для загрузки информации о выезжающем и передаем пользователю в качестве параметра, используя call - эффект.

После того, как loadDeparture закончен, мы выполняем loadFlight с объектом отправляемого, выбранным в предыдущей операции.

То же самое будет с прогнозом, нам нужно подождать, пока рейс будет загружен, чтобы выполнить следующий call - эффект.

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

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

Довольно аккуратно, а?

Теперь давайте проверим другой случай. Рассмотрим, что getFlight и getForecast могут запускаться одновременно. Им не нужно ждать завершения друг друга, чтобы запуститься самому, поэтому мы можем создать разные панели для этого случая.

Не блокирующая сага

Чтобы выполнить две неблокирующие операции, нам нужно внести небольшую правку в нашу предыдущую сагу:

function* loadDashboardNonSequenced() {
  try {
    //Wait for the user to be loaded
    yield take('FETCH_USER_SUCCESS');
    //Take the user info from the store
    const user = yield select(getUserFromState);
    //Get Departure information
    const departure = yield call(loadDeparture, user);
    //Here is when the magic happens
    const [flight, forecast] = yield [call(loadFlight, departure.flightID), call(loadForecast, departure.date)];
    //Tell the store we are ready to be displayed
    yield put({type: 'FETCH_DASHBOARD2_SUCCESS', payload: {departure, flight, forecast}});
} catch(error) {
    yield put({type: 'FETCH_FAILED', error: error.message});
  }
}

Мы должны регистрировать yield генератор как массив:

const [flight, forecast] = yield [call(loadFlight, departure.flightID), call(loadForecast, departure.date)];

Таким образом, обе операции вызываются параллельно, но в конце дня мы сможем дождаться завершения обоих и обновить интерфейса.

Затем нам нужно зарегистрировать сагу в rootSaga:

function* rootSaga() {
  yield[
    fork(loadUser),
    takeLatest('LOAD_DASHBOARD', loadDashboardSequenced),
    takeLatest('LOAD_DASHBOARD2' loadDashboardNonSequenced)
  ];
}

Что делать, если нам нужно обновить интерфейс сразу после завершения операции? Не беспокойтесь я вернусь.

Не последовательные и не блокирующие саги

Мы также можем изолировать наши саги друг от друга и объединить их, то есть они могут работать независимо. Это именно то, что нам нужно. Давайте взглянем.

Шаг №1: Мы изолируем Саги Прогноза и Полета. Обе они зависят от отъезда.

/* **************Flight Saga************** */
function* isolatedFlight() {
  try {
    /* departure will take the value of the object passed by the put*/
    const departure = yield take('FETCH_DEPARTURE3_SUCCESS');
 
    const flight = yield call(loadFlight, departure.flightID);
 
    yield put({type: 'FETCH_DASHBOARD3_SUCCESS', payload: {flight}});
  } catch (error) {
    yield put({type: 'FETCH_FAILED', error: error.message});
  }
}

/* **************Forecast Saga************** */
function* isolatedForecast() {
    try {
      /* departure will take the value of the object passed by the put*/
     const departure = yield take('FETCH_DEPARTURE3_SUCCESS');
     const forecast = yield call(loadForecast, departure.date);
     
     yield put({type: 'FETCH_DASHBOARD3_SUCCESS', payload: { forecast, }});
} catch(error) {
      yield put({type: 'FETCH_FAILED', error: error.message});
    }
}

Обратили внимание на что-то очень важное здесь? Это то как мы спроектировали наши саги:

  • Они оба ждут того же Action Event (FETCH_DEPARTURE3_SUCCESS), чтобы начать.
  • Они получат значение при срабатывании этого события. Подробнее об этом на следующем шаге.
  • Они будут выполнять свою асинхронную операцию с использованием call - эффекта, и оба будут запускать одно и то же событие после завершения. Но они оба отправляют разные данные в хранилище. Благодаря силе Redux мы можем сделать это без каких-либо изменений в нашем редьюсере.

Шаг №2: Давайте сделаем изменения в последовательности отправляемого и убедимся, что он шлет значение отправляемого в обе саги:

function* loadDashboardNonSequencedNonBlocking() {
  try {
    //Wait for the action to start
    yield take('FETCH_USER_SUCCESS');
    //Take the user info from the store
    const user = yield select(getUserFromState);
    //Get Departure information
    const departure = yield call(loadDeparture, user);
    //Update the store so the UI get updated
    yield put({type: 'FETCH_DASHBOARD3_SUCCESS', payload: { departure, }});
    //trigger actions for Forecast and Flight to start...
    //We can pass and object into the put statement
    yield put({type: 'FETCH_DEPARTURE3_SUCCESS', departure});
  } catch(error) {
    yield put({type: 'FETCH_FAILED', error: error.message});
  }
}

Здесь ничего особенного, пока мы не доберемся до put - эффекта. Мы можем передать объект экшенам, и он будет помещен из генератора yield в константу const departure, которая будет передана в саги рейса и прогноза . Мне это нравится.

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

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

Что на счет тестирования?

Вы ведь тестируете свой код...правда?

Саги легко тестируются, но они сочетаются с вашими шагами, устанавливаются в последовательности из-за природы генераторов. Давайте посмотрим пример. (И не стесняйтесь проверить все тесты в папке sagas репозитория ):

describe('Sequenced Saga', () => {
  const saga = loadDashboardSequenced();
  let output = null;
it('should take fetch users success', () => {
      output = saga.next().value;
      let expected = take('FETCH_USER_SUCCESS');
      expect(output).toEqual(expected);
  });
it('should select the state from store', () => {
      output = saga.next().value;
      let expected = select(getUserFromState);
      expect(output).toEqual(expected);
  });
it('should call LoadDeparture with the user obj', (done) => {
    output = saga.next(user).value;
    let expected = call(loadDeparture, user);
    done();
    expect(output).toEqual(expected);
  });
it('should Load the flight with the flightId', (done) => {
    let output = saga.next(departure).value;
    let expected = call(loadFlight, departure.flightID);
    done();
    expect(output).toEqual(expected);
  });
it('should load the forecast with the departure date', (done) => {
      output = saga.next(flight).value;
      let expected = call(loadForecast, departure.date);
      done();
      expect(output).toEqual(expected);
    });
it('should put Fetch dashboard success', (done) => {
       output = saga.next(forecast, departure, flight ).value;
       let expected = put({type: 'FETCH_DASHBOARD_SUCCESS', payload: {forecast, flight, departure}});
       const finished = saga.next().done;
       done();
       expect(finished).toEqual(true);
       expect(output).toEqual(expected);
    });
});

Убедитесь, что вы импортируете все хелперы эффектов и функций, которые вы собираетесь тестировать.

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

За сценой каждый генератор переходит к следующей строке после выхода, когда вызывается следующий метод. Вот почему мы используем значение saga.next().

Эта последовательность задана жестко. Если вы измените шаги в саге, тест не пройдет.

Вывод.

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

Я нашел thunks более легким для внедрения и поддержки. Но для более сложной работы Redux-Saga выполняет отличную работу.

Еще раз, я благодарю Томаса за вдохновение для этого поста. Надеюсь, кто-то найдет столько вдохновения в этом посте, сколько нашел я :).

Если у вас есть вопросы, не стесняйтесь твитнуть мне. Я рад помочь.

Материал взят с Async operations using redux-saga

четверг, 20 июля 2017 г.

Что необходимо знать
Junior React Developer-у
при устройству на работу.

Нашел вразумительные требования для разработчика здесь: https://toster.ru/q/408785

React
0) Какую проблему решает react ?

1) Мгновенно ли срабатывает setState? Если нет, то как выполнить код, который 100% выполнится после того, как новый state будет установлен?

2) Зачем многие постоянно пишут в constructor: this.FUNCTION_NAME = this.FUNCTION_NAME.bind(this) и отсюда вопрос вытекает чему равно this в разных местах вашего компонента...

3) В каких методах жизненного цикла стоит выполнять xhr запросы? В каких стоит "обновлять state на основе props"?

4) Что будет если вызвать this.setState в render методе компонента?

5) Зачем нужен componenWIllUnmount, приведите пример.

6) Контролируемые, не контролируемые компоненты

7) Как организовать роутинг в реакт приложении? (ответ: взять react-router - подходит, но было бы круто, если бы вы рассказали, как он примерно работает)*

8) Зачем нужны propTypes? Что происходит с ними в production сборке?

9) Как можно удобно "отлаживать" чужой код приложения, написанного на react (намек в сторону React devtools)




Redux
0) Какую проблему решает redux?


1) Зачем многие создают типы действий NAME_REQUEST / NAME_SUCCESS ? А заодно, что такое "действие", а что такое "создатель действия"...

2) Что такое редьюсер? Можете написать простой редьюсер без react/redux?*

3) Для чего нужен redux-thunk? Как он работает? Напишите (можно псевдокод) асинхронный создатель действия (либо, если надоело говорить "терминами" - асинхронный aciton)

4) Как компоненты приложения получают "пропсы" из "стора"?*


5) Можно ли (и считается ли это нормальным) использовать state, если используется Redux?

6) Почему в reducer'ax мы возвращаем новые объекты? Приведите пример, когда вы возвращаете новый объект, а когда тот же самый.

6.5) А так же, "как в js вообще это работает?".

Например:
let obj1 { name: 'Test', age: 100 }
let obj2 = obj1
obj2.name = 'Test_new'

Что будет в obj1, почему? В каких случаях объекты могут быть равны?


7) Что возвращает функция connect (из react-redux)?




Общее:
0) package.json
1) Webpack, gulp, etc...
2) Node.js
3) Promise

Что-нибудь практическое:
1) Как бы вы валидировали форму, если ошибки валидации приходят после submit'a ее на сервер.
2) Почему не работает следующий код, сделайте чтобы работало

...
На истину не претендую, но такие вопросы имели место быть на собеседованиях. В беседе можно многое разузнать дополнительными вопросами и так далее. Так же, если часть вопросов вам неизвестна - не беда, многие и на половину ответить не могут.

p.s. звездочкой отметил, на мой взгляд не самые необходимые для junior-собеседования вопросы.    




React:

0) Какую проблему решает react ?

Вы описываете как сейчас должен выглядеть DOM. Реакт берет на себя работу по определению необходимых действий чтобы DOM на самом деле стал выглядеть так, как вы сказали.


1) Мгновенно ли срабатывает setState? Если нет, то как выполнить код, который 100% выполнится после того, как новый state будет установлен?

Судя по официальной документации setState () является скорее запросом, чем немедленной командой для обновления компонента. Для лучшей производительности React может задержать ее, а затем обновить несколько компонентов за один проход. React не гарантирует немедленного применения изменений состояния. На вопрос "как гарантировано получить текущее состояние и обновить его" подробно отвечают здесь, если вкратце то просто передать функцию в setState, которая в момент выполнения обратиться к текущему state и обновит его:
submit(){
   this.setState((prevState, props)=>{
      return {showForm: !prevState.showForm}
   });
}

2) Зачем многие постоянно пишут в constructor: this.FUNCTION_NAME = this.FUNCTION_NAME.bind(this) и отсюда вопрос вытекает чему равно this в разных местах вашего компонента...

Судя по изложенному здесь:
React и ES6 - Часть 3, Биндинг методов в классах React (включая ES7)
Function.prototype.bind()
в случае с React если вызвать напрямую this.myFunction то this будет undefined, так как функция вызывается не в контексте класса, а в контексте объекта класса. Поэтому приходится биндить функции члены класса к текущему контексту в конструкторе, в момент создания объекта класса, чтобы объект увидел функции описанные в классе. Самый короткий варинат записи с использованием es7 и функцией стрелкой выглядит так:
myFunc = () => {код функции};
И писать ее нужно уже не в конструкторе, а прямо в теле класса.

3) В каких методах жизненного цикла стоит выполнять xhr запросы? В каких стоит "обновлять state на основе props"?

В какой момент жизненного цикла применяется AJAX запрос и почему?
AJAX запросы должны идти в момент события componentDidMount.

Для этого есть несколько причин:
- Следующая реализация алгоритма сверки в React будет иметь возможность запускать и останавливать рендеринг для повышения производительности. Одним из компромиссов является то, что componentWillMount, другой цикл событий, где, возможно, стоит реализовать AJAX-запрос, будет “не детерминированным”. Это означает то, то React может начать вызывать componentWillMount в разное время, когда он чувствует в этом необходимость. Это, очевидно, плохая формула для создания AJAX-запроса.

- Вы не можете гарантировать, что AJAX-запрос не будет разрешен (resolve) перед моментом монтирования компонента. Если да, то это будет означать, что вы пытаетесь выполнить setState над демонтированным компонентом, который не только не работает, но и сам React будет ругаться на вас. Делайте AJAX-запросы в componentDidMount, чтобы гарантировать, что есть компонент для обновления.
Проще говоря, если ваш запрос приносит новые данные и они должны отразиться в данном компоненте, то компонент должен быть отрендерен, о чем нам и сообщает событие componentDidMount.

А обновлять state из приходящих props надо в componentWillReceiveProps(nextProps), где приходят новые props и который вызывается до монтирования в компонент новых пропсов. Жизненный цикл компонента

4) Что будет если вызвать this.setState в render методе компонента?

setState вызовет обновление компонента, что повлечет снова вызов метода render, который снова вызовет setState, и процесс зациклится.

5) Зачем нужен componenWIllUnmount, приведите пример.

componenWIllUnmount вызывается перед удалением компонента из виртуального DOM дерева, что может расцениваться как деструктор в других языках. Функции очистки памяти, удаления неиспользуемых объектов могут быть вызваны в componenWIllUnmount
пример таймер обновления сообщений в чате и его очистка:
componentDidMount: function(){
    this.timer = setInterval(this.tick, 50);
},
componentWillUnmount: function(){
    clearInterval(this.timer);
}

6) Контролируемые, не контролируемые компоненты

В чем разница между controlled и uncontrolled компонентами? Контейнеры и компоненты Проще говоря, контролируемые компоненты работают через стейты, получают и пишут данные обычно в стейты. А Неконтролируемые компоненты работают напрямую с виртуальным DOM деревом, обычно через ссылку ref. Дух React делает упор на контролируемых компонентах.

7) Как организовать роутинг в реакт приложении? (ответ: взять react-router - подходит, но было бы круто, если бы вы рассказали, как он примерно работает)*

React-Router в общем определении можно назвать набором компонентов определяющих на основе текущего пути, какой компонент будет выводиться.
Можно разделить 2 версии роутера - 3ю и 4ю. В 4й версии упор сделан больше на декларативный стиль и роутер должен быть компонентом верхнего уровня во всех компонентах (на мой взгляд порог вхождения в 4й роутер выше чем в 3й, возможно из-за новизны и небольшого числа источников информации об особенностях 4го роутера).

React-Router v3.x
react-урок-5-маршрутизация
Погружение в React: роутер
ReactTraining v3. docs
<Router>
  <Route path="/" component={Home} />
  <Route path="/users" component={Users} />
  <Route path="/widgets" component={Widgets} />
</Router>


React-Router v4.x
react-router v4.x.x – заметки по использованию
react-router v4.x.x – заметки по использованию
Простой туториал React Router v4
Мигрантам с 3го на 4й
https://reacttraining.com
<BrowserRouter>
  <div>
    <Switch>
      <Route exact path='/' component={Home}/>
      <Route path='/about' render={() => <h1>About</h1>}/>
      <Route render={() => <h1>Page not found</h1>}/> 
    </Switch>
  </div>
</BrowserRouter>



8) Зачем нужны propTypes? Что происходит с ними в production сборке?

propTypes задают типы входных параметров для отрисовываемого компонента, чтобы в случае несовпадения, например вместо числа пришла строка получить ошибку в react-dev-tools и отловить этот момент во время выполнения. Это была встроенная возможность контроля типов для больших приложений, но с недавнего времени вынесена в отдельный пакет.

PropTypes — проверка типов в React
MyComponent.propTypes = {
  optionalArray: React.PropTypes.array,
  optionalBool: React.PropTypes.bool,
  optionalFunc: React.PropTypes.func,
  optionalNumber: React.PropTypes.number
}

9) Как можно удобно "отлаживать" чужой код приложения, написанного на react (намек в сторону React devtools)

React devtools поставляется в двух видах, отдельным пакетами и расширением для популярных браузеров. В расширении можно увидеть изменения состояний приложения и узлы виртуального DOM-дерева.
Добавляем redux-dev-tools
React Developer Tools
Также отлаживать можно в:
IDE WebStorm: Отладка внутри IDE WebStorm
Visual Studio Code: Как настроить отладку CRA внутри VSCode


Redux



0) Какую проблему решает redux?

Работа с Redux С ростом сложности веб-приложений усложняется задача обновления и вывода их базовых данных. Многие подходы по управлению этими данными приводят к сложной паутине представлений. Эти представления могут прослушивать обновления различных моделей, отправляющих свои изменения еще большему количеству представлений. Все это оставляет разработчиков с непрозрачным и непредсказуемым кодом, который практически невозможно изменить, не забыв при этом прикрепить прослушиватель к какому-нибудь важному элементу. Redux предоставляет механизм взаимодействия компонентов любого уровня между собой через хранилище, что позволяет сосредоточиться не на взаимодействии дочерних и родительских частей, а на логике самого приложения.
1) Зачем многие создают типы действий NAME_REQUEST / NAME_SUCCESS ? А заодно, что такое "действие", а что такое "создатель действия"...

NAME_REQUEST / NAME_SUCCESS / NAME_FAILURE всего лишь общепринятый шаблон именования идентификаторов действий. Просто для понятности при отладке, вывалился запрос с ошибкой или прошел успешно и пришедшие данные стоит считывать.
В стандартной парадигме flux действие это плоский JS объект, имеющий поле type. Действие может иметь свойства error, property,meta. Других полей объект действия иметь не должен. Создатель действия это функция, возвращаемый результат которой - объект Действие.
2) Что такое редьюсер? Можете написать простой редьюсер без react/redux?*

Редьюсер это синглтон шаблон проектирования, проще говоря единственный объект хранящий все состояние приложения, в актуальном виде.
Написание редьюсера сводиться к созданию глобального объекта с функциями записи чтения его свойств, и диспетчеризации шины сообщений между взаимодействующими компонентами.
В более сложном приложении Вы, скорее всего, будете иметь разные сущности, которые будут ссылаться друг на друга. Мы советуем поддерживать состояние (state) в настолько упорядоченном виде, насколько это возможно. Старайтесь не допускать никакой вложенности. Держите каждую сущность в объекте, который хранится с ID в качестве ключа. Используйте этот ID в качестве ссылки из других сущностей или списков. Думайте о состоянии приложения (apps state), как о базе данных.
Изучаем Redux на примере создания мини-Redux Structuring Reducers
3) Для чего нужен redux-thunk? Как он работает? Напишите (можно псевдокод) асинхронный создатель действия (либо, если надоело говорить "терминами" - асинхронный aciton)

redux-thunk позволяет сделать вызов Действий асинхронным. redux-thunk возвращает функцию вместо действия.
Асинхронные actions
Сам action возвращающий функцию:
export function getPhotos(year) {
  return (dispatch) => {
    dispatch({
      type: GET_PHOTOS_REQUEST
    })

    $.ajax(url)
      .success(
        dispatch({
          type: GET_PHOTOS_SUCCESS,
          payload: response.photos
        })
      )
      .error(
        dispatch({
          type: GET_PHOTOS_FAILURE,
          payload: response.error,
          error: true
        })
      )
  }
}
, которая будет выполнена если она функция или возвращен плоски объект action-если он не функция.

function thunkMiddleware({ dispatch, getState }) {
  return next => action =>
    typeof action === 'function' ?
      action(dispatch, getState) :
      next(action);
}
module.exports = thunkMiddleware


4) Как компоненты приложения получают "пропсы" из "стора"?*

Вызывается функция связывания 'connect'. Функция связывает экспортируемый компонент с функцией меппинга данных стора на параметры props.
function mapStateToProps (state) {
  return {
    user: state.user
  }
}
export default connect(mapStateToProps)(App)

5) Можно ли (и считается ли это нормальным) использовать state, если используется Redux?

Использование state при живом redux позволяет смапить в компонент разные части стора и обновлять их независимо друг от друга.

6) Почему в reducer'ax мы возвращаем новые объекты? Приведите пример, когда вы возвращаете новый объект, а когда тот же самый.

Возвращение новых объектов это идеология Immutable приложения, с однонаправленным потоком данных. Это дает:
Простое и быстрое отслеживание изменений.
Возьмем пример с state. После модификации объекта state нужно сравнить его с объектом nextState и узнать, что конкретно в нем изменилось. Иммутабельность сильно упрощает нам задачу: вместо того, чтобы сравнивать значение каждого поля каждого вложенного в state объекта с соответствующим значением из nextState, можно просто сравнивать ссылки на соответствующие объекты и отсеивать таким образом целые вложенные ветки сравнений.

Безопаснее использовать и легче тестировать.
Если пришедшие данные испорчены, то в момент сравнения со старым стейтом, можно это выявить.

Например если добавлять элемент в массив push-ом и возвращать его то это будет тот же самый объект, а если вызовом concat то будет создан новый массив-копия оригинала и к нему будет добавлен элемент. Это так называемый неизменяющий метод.
Не изменяйте состояние, а копируйте его

6.5) А так же, "как в js вообще это работает?".
Например:
let obj1 { name: 'Test', age: 100 }
let obj2 = obj1
obj2.name = 'Test_new'

Что будет в obj1, почему? В каких случаях объекты могут быть равны?


obj2-получит ссылку на obj1 и сможет изменять все поля, на которые указывает obj1. Поэтому в obj1.name будет сидеть 'Test_new'. а равны объекты будут, когда obj1 и obj2 будут ссылаться на разные объекты, но все значения полей объектов будут равны.


7) Что возвращает функция connect (из react-redux)?

connect декоратор возвращает компонент-контейнер. Компоненты-контейнеры существуют для того, чтобы презентационные компоненты могли сосредоточиться на представлении и не думать о состоянии. Они также передают состояние в дочернее представление как свойства. И это именно то, что делает connect() — передает состояние (через свойства) в презентационный компонент и возвращает компонент-обертку для презентационного компонента. По сути эта обертка и есть компонент-контейнер.
Пропуск контейнера

Общее



0) package.json

Файл package.json содержит в себе информацию о вашем приложении: название, версия, зависимости и тому подобное. Любая директория, в которой есть этот файл, интерпретируется как Node.js-пакет, даже если вы не собираетесь публиковать его. Способ использования файла package.json зависит от того, собираетесь ли вы скачивать пакет или публиковать его.
простейший вид выглядит так:
{
  "devDependencies": {
    "название пакета из репозитория npmjs.com": "Версия пакета",
    "еще пакет": "1.0.1"
  }
}
В папке с файлом package.json вызывается команда 'npm i' и пакеты скачиваются из репозитория в папку node_modules в готовом для использования виде

1) Webpack, gulp, etc...

Webpack можно охарактеризовать, как “сборщик модулей”. Он берет JavaScript модули с необходимыми зависимостями, и затем соединяет их вместе как можно более эффективным способом, на выходе создавая единый JS-файл.
Введение в Webpack, часть 1


Gulp - это инструмент, который помогает автоматизировать рутинные задачи веб-разработки.
Gulp для самых маленьких - подробное руководство
Gulp предназначен для решения таких задач, как:
  1. Создание веб-сервера и автоматическая перезагрузка страницы в браузере при сохранении кода, слежение за изменениями в файлах проекта;
  2. Использование различных JavaScript, CSS и HTML препроцессоров (CoffeeScript, Less, Sass, Stylus, Jade и т.д.);
  3. Минификация CSS и JS кода, а также, оптимизация и конкатенация отдельных файлов проекта в один;
  4. Автоматическое создание вендорных префиксов (приставок к названию CSS свойства, которые добавляют производители браузеров для нестандартных свойств) для CSS.
  5. Управление файлами и папками в рамках проекта - создание, удаление, переименование;
  6. Запуск и контроль выполнения внешних команд операционной системы;
  7. Работа с изображениями - сжатие, создание спрайтов, ресайз (png, jpg, svg и др.);
  8. Деплой (отправка на внешний сервер) проекта по FTP, SFTP, Git и т.д.
  9. Подключение и использование в проекте безгранично большого количества Node.js и Gulp утилит, программ и плагинов.
  10. Создание различных карт проекта и автоматизация другого ручного труда.



Grunt — это инструмент для сборки javascript проектов из командной строки с использованием задач. Grunt
2) Node.js

Node или Node.js — программная платформа, основанная на движке V8 (транслирующем JavaScript в машинный код), превращающая JavaScript из узкоспециализированного языка в язык общего назначения. Node.js добавляет возможность JavaScript взаимодействовать с устройствами ввода-вывода через свой API (написанный на C++), подключать другие внешние библиотеки, написанные на разных языках, обеспечивая вызовы к ним из JavaScript-кода. Node.js применяется преимущественно на сервере, выполняя роль веб-сервера. В основе Node.js лежит событийно-ориентированное и асинхронное (или реактивное) программирование с неблокирующим вводом/выводом.
Скринкаст
3) Promise

В своей основе обещания (Promise) слегка похожи на события за исключением:
  • Обещание может завершиться только один раз, либо успехом, либо ошибкой, и другого не дано. Оно не может выполниться дважды.
  • Если обещание выполнится, и ты только после этого передашь ему колбэк, он отработает, не взирая на то, что событие уже давно произошло.

Это чрезвычайно удобно для асинхронных запросов, т.к. неинтересно, когда там это именно случилось, а интересно обработать результаты произошедшего. Promise Также общения можно строить в пул:
Promise.all(Promise1,Promise2...).than(()=>{alert('all ok');}).catch((err)=>{console.log(err);});

Что-нибудь практическое

:

1) Как бы вы валидировали форму, если ошибки валидации приходят после submit'a ее на сервер.

Есть интересное решение для валидации компонентов чтобы при перерендеринге компоненты не теряли свойства - Весь компонент форм построен по принципу единой шины обмена событиями, где сама форма и поля ввода используют один и тот же канал для обмена данными о состоянии модели и валидации. Валидация форм на React JS by Ast
Этот метод хорошо подходят для динамических форм изменяющихся в процессе ввода данных.

А это более простой пример с привязкой к props: Валидация форм в ReactJS
2) Почему не работает следующий код, сделайте чтобы работало

Во-первых в коде функция обработчик находится вне контекста, и ее надо забиндить в конструкторе к this
constructor(props) {
    super(props)
    this.state = {
      count: 1
    }
    this.handler=this.handler.bind(this);
  }

Во вторых инкремент state выполняется после присвоения в count и нужно просто перенести знак налево
  handler() {
    this.setState({count: ++this.state.count})
  }


ответы были подобраны в соответствии с моим понимаем Juniora основ Reacta.