25 октября 2015 г. — Обновлено 21 ноября 2017 г.

Этот пост был процитирован с Github.

Этот учебник был написан, чтобы сделать простое введение в Redux. Проект был разработан с использованием ES6 и ReactJS, обязательными условиями являются ES6 и ReactJS.

Демонстрация проекта

Установка

Клонируйте репозиторий и установите зависимости.

git clone https://github.com/yildizberkay/redux-example.git 
cd redux-example
npm install 
npm start 
open http://localhost:3000

ESLint

npm run lint

Зачем нам нужен Redux?

Давайте подумаем об обычном приложении React. Есть компоненты, состояния, реквизиты и действия API. Компонент вызывает другие компоненты, и если вы запускаете состояние пользовательского интерфейса, обновляется представление и т. д. Наконец, приложение становится сложным, и вы не можете им управлять.

React – это библиотека JavaScript для создания пользовательских интерфейсов в Facebook и Instagram. Многие считают React буквой V в MVC.

Источник: https://facebook.github.io/react/docs/why-react.html

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

В Redux есть 3 основные части: действия, редукторы и хранилище.

Действия

Действия используются для получения данных. В этой части можно делать запросы к API.

Действия — это полезные данные, которые отправляют данные из вашего приложения в ваш магазин. Это единственный источник информации для магазина.

Источник: https://redux.js.org/docs/basics/Actions.html

В следующем примере есть действие с именем searchPhotoAction. Это действие возвращает функцию, содержащую searchWithPhotoAPI и функцию dispatch, уведомляющую store. Как видите, в первую очередь dispatch отправляет тип «SEARCH_PENDING», это показывает счетчик на экране. После этого, если запрос выполнен успешно, dispatch отправляет «SEARCH_DONE», массив фотографий и другую информацию.

function searchWithPhotoAPI(keyword, page, dispatch) {
  if (page >= 2) {
    dispatch({
      type: types.SEARCH_PENDING_FOR_NEXT,
    });
  } else {
    dispatch({
      type: types.SEARCH_PENDING,
    });
  }
  photoSearch(keyword, page, (data) => {
    dispatch({
      type: types.SEARCH_DONE,
      photos: data.photos,
      page,
      keyword,
    });
  });
}

export function searchPhotoAction(keyword, page = 1) {
  return (dispatch) => {
    searchWithPhotoAPI(keyword, page, dispatch);
  };
}

Редукторы

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

В следующем коде возвращаемое состояние изменяется в соответствии с action.type. Если action.type равно types.SEARCH_PENDING, мы отобразим счетчик.

const searchPhotos = (state = initialState, action) => {
  switch (action.type) {
    case types.SEARCH_DONE:
      return {
        ...state,
        photos: [...state.photos, ...action.photos],
        status: 'DONE',
        page: action.page,
        keyword: action.keyword,
      };
    case types.SEARCH_PENDING_FOR_NEXT:
      return {
        ...state,
        status: 'PENDING_FOR_NEXT',
      };
    case types.SEARCH_PENDING:
      return {
        ...state,
        photos: [],
        status: 'PENDING',
      };
    default:
      return state;
  }
}

Магазин

В Redux есть только один магазин. Его можно создать напрямую с помощью createStore, а также с помощью applyMiddleware.

// containers/App.jsx

const reducer = combineReducers({ searchPhotos });
const store = createStore(reducer, {}, applyMiddleware(thunk));

const App = () => (
  <div>
    <Provider store={store}>
      <SearchApp />
    </Provider>
  </div>
)

Вызов действий

Прежде чем вызывать действие, мы должны объединить действия и функцию отправки. Функция connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) подключает компонент React к хранилищу Redux.

bindActionCreators(actionCreators, dispatch) объединяет действия и функцию отправки.

// containers/SearchApp.jsx

...

class SearchApp extends PureComponent {
  render() {
    const actions = bindActionCreators(action, this.props.dispatch);
    return (
      <div>
        <div id="header" className="header">
          <div className="container">
            <div className="row">
              <div className="col-md-6 col-md-offset-3 search-bar-content">
                <h1>Search on 500px</h1>
                <SearchInput actions={actions} status={this.props.status} />
              </div>
            </div>
          </div>
        </div>
        <div className="container">
          <PhotoList actions={actions} photos={this.props.photos} status={this.props.status} />
        </div>
      </div>
    );
  }
}

...

export default connect(
  mapStateToProps,
)(SearchApp);

Теперь вы можете вызывать действия с помощью следующего кода.

actions.searchPhotoAction(keyword, page)

Структура каталогов

Root
├── public
│   ├── actions
├── src
│   ├── actions
│   │   └──
│   ├── api
│   │   └──
│   ├── components
│   │   └──
│   ├── constants
│   │   └──
│   ├── containers
│   │   └──
│   ├── reducers
│   │   └──
│   ├── stylesheets
│   │   └──
│   └── index.js
├── .eslintignore
├── .eslintrc.json
├── package.json
└── README.md

Ресурсы

Список изменений

21 ноября 2017 г.

  • README обновление.
  • Обновление версии ReactJS. [15.6.2]
  • Обновление редукс-версии. [3.7.2]
  • Миграция React-скриптов.

25 октября 2015 г.

  • Получить поддержку для всех браузеров. @pcanterini PR
  • Добавлены правила Airbnb ESLint.

12 октября 2015 г.

  • Начальная фиксация.

Первоначально опубликовано на yildizberkay.com 25 октября 2015 г.