Создание одностраничного приложения CRUD с использованием React и Redux может быть сложной задачей, потому что вам придется иметь дело с новыми методами и терминами, такими как редукторы, действия, промежуточное ПО, магазины и т. Д. .

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

В этом блоге я представлю общий подход к созданию приложения для блога с тремя страницами и возможностью навигации по ним.

Кроме того, я также установлю шаблон для выполнения асинхронных запросов и обработки четырех асинхронных состояний: «загрузка», «успех», «ошибка» и «успех и навигация».

Исходный код: https://github.com/rajaraodv/react-redux-blog

Приложение Live: https://protected-escarpment-79486.herokuapp.com/

Twitter: https://twitter.com/rajaraodv (@rajaraodv)

Давайте начнем.

ШАГ 1 - Напишите подробные макеты для каждой страницы и каждой фазы.

В нашем приложении есть 3 страницы: страница Индекс, на которой отображается список сообщений, страница Сведения о публикации и страница Новое сообщение.

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

1.1 Фаза успеха - подробные фиксации, когда что-то работает

Примечание: вы можете щелкнуть изображения для увеличения и прочитать

1.2 Фаза загрузки - подробные макеты при загрузке

1.3 Фаза ошибки - подробные фиксации при возникновении ошибки

ШАГ 2 - Разделите каждую страницу на компоненты

Посмотрите на все этапы каждой страницы и примерно разделите каждую страницу на компоненты на основе «цели» и на основе физического местоположения.

Это поможет вам идентифицировать повторно используемые компоненты на разных страницах, а также любые дополнительные на определенном этапе. Например, вам может понадобиться компонент «Spinner» или «Ошибка» для разных фаз.

Примечание: это не обязательно должно быть идеально. Вы можете внести изменения позже.

2.1 - Этап успеха: разделите каждую страницу на компоненты

  1. Индексная страница: 1. Показывает список сообщений, 2. Позволяет перейти на страницу формы сообщения для создания нового сообщения. В итоге мы получаем компоненты PostsList и Header.
  2. Страница сведений о публикации: 1. Показывает сведения о публикации, 2. Позволяет вернуться на страницу указателя. 3. Страница указателя. В итоге мы получаем компоненты PostDetails и Header.

3. Страница нового сообщения: 1. Позволяет создавать сообщения и 2. позволяет вернуться к Индексу. И снова мы получаем компоненты PostForm и Header.

Обратите внимание, что мы можем повторно использовать заголовок на всех трех страницах из-за физического расположения. Таким образом, мы получаем четыре компонента (вместо 6): 1. Компоненты PostsList, 2. PostDetails, 3. PostForm и 4. Header.

2.2 - Фаза загрузки: разделите каждую страницу на компоненты

Если вы посмотрите на макет, мы просто отображаем текст «загрузка ...» на каждой странице, а не используем какой-то причудливый счетчик или «тост». Так что у нас больше нет компонентов.

2.3 - Фаза ошибки: разделите каждую страницу на компоненты

Если вы посмотрите на макет, мы просто выдаем всплывающее окно с предупреждением и не используем никаких настраиваемых модальных окон. Так что у нас больше нет компонентов.

Итак, net-net, у нас есть 4 компонента (1. PostsList, 2. PostDetails, 3. PostForm и 4. Заголовок)

Условия Redux:

Термины Redux - «Действия» и «Состояния»

Каждый компонент выполняет две функции:
1. Слушайте события пользователя и сервера и отправляйте их функциям JS. В Redux события представлены в виде объекта JSON под названием «Действия».

{"type": "FETCH_POST", "id": 1234} // <-- Action

2. Визуализировать DOM на основе некоторых данных. Эти данные называются «состоянием», которое также является объектом JSON.

{"post": {"id": 1234, "title": "My Redux Post"}} // <-- state

Условия Redux - «Создатели действий»

Это функции, которые прослушивают события DOM или сервера и возвращают формальный объект JSON «Action».

function fetchPost(id) {
return {
 type: FETCH_POST,
 result: makeServerRequest("http://postsServer.com/api/id")
 };
}

Смотрите Создатели действий для нашего приложения.

Условия Redux - «Отправка действия»

Redux предоставляет функцию под названием «dispatch», которая позволяет нам передавать объект JSON «Action» всем остальным компонентам. Отправка действия означает простой вызов функции отправки с объектом JSON действия.

//Call the "Action Creator" w/ post's id and then use it's return //value (Action JSON object) to finally dispatch it to "reducers"
dispatch(fetchPost(id)) or dispatch({type:"FETCH_POST", id:1234})

Компоненты вызывают создателей действий для получения действий, а затем отправляют действия. Затем Redux отправляет действия в «Редукторы».

Термины Redux - «Редукторы»

Редукторы - это функции, которые выполняют действие и текущее состояние, которое было отправлено им через «отправку», применяют действие к текущему состоянию и возвращают новое состояние. И Redux повторно визуализирует все компоненты всякий раз, когда появляется новое состояние.

//If the action is FETCH_POST_SUCCESS, return a new "activePost" //state w/ new post)
case FETCH_POST_SUCCESS:
 return {activePost: {post: action.payload.data, error:null,     
                     loading: false}};

См. Редукторы reducer_posts.js и основной index.js (он объединяет несколько редукторов в один)

Хорошо, прежде чем мы перейдем к шагу 3, давайте разберемся, как бороться с асинхронными действиями, поскольку каждая страница выполняет вызовы AJAX.

ШАБЛОН: работа с асинхронными действиями

Если компонент загружает объект (например, список сообщений) через вызов AJAX на сервер, состояние этого объекта должно отслеживать все возможные состояния. Начальное состояние таких объектов должно выглядеть так: {objName: {obj: null, loading: false, error: null}}.

Кроме того, такие компоненты должны отправлять до 4 действий, таких как «FETCH_OBJ» (для загрузки), «FETCH_OBJ_SUCCESS», «FETCH_OBJ_FAILURE» и « OBJ_RESET »(чтобы очистить предыдущее грязное состояние).

Например, если мы загружаем список сообщений ..

Примечание: вы можете щелкнуть изображения, чтобы увеличить и прочитать

Начальное состояние: начальное состояние должно выглядеть так,

{postsList:{posts:[], loading:false, error:null}}

Действия:

  1. FETCH_OBJ - отправьте это, чтобы сделать запрос к серверу, а также сообщить другим компонентам, что мы загружаем. Это помогает текущим / другим компонентам показывать «загрузку», скрывать или что-то делать.
dispatch({“type”: “FETCH_POSTS”, loading: true})

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

{postList: {posts:null, error: null, loading: true}}

2. FETCH_OBJ_SUCCESS: отправьте его, когда получите успешный ответ. Это нужно для отображения фактических данных, а также для отмены «загрузки».

dispatch({"type": "FETCH_POSTS_SUCCESS", "posts":[post1, post2])

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

{postsList:{posts:[post1, post2], error:null, loading: false}}

3. FETCH_OBJ_FAILURE: отправьте это сообщение, если вы получите неудавшийся ответ. Это необходимо для отображения сообщения об ошибке, а также для отмены «загрузки».

dispatch({"type": "FETCH_POSTS_FAILURE", "error": "Error message"})

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

{postList:{posts:null, error:{msg: "Error msg"}, loading: false}}

4. RESET_OBJ: отправьте это сообщение для сброса состояния компонента после успеха / неудачи. Это необязательно, но может быть полезно, если вы хотите повторно использовать «грязный» компонент из предыдущего запроса AJAX.

dispatch({"type": "RESET_POST", loading: false, "post": null, "error": "Error message"})

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

{postList:{post:null, error:null, loading: false}}

ШАГ 3 - Составьте список состояний и действий для каждого компонента (И для каждой фазы)

Взгляните на каждый компонент по отдельности, на каждую фазу и список состояний и действий.

У нас есть 4 компонента: 1. Компоненты PostsList, 2. PostDetails, 3. PostForm и 4. Header.

3.1 Компонент PostList - Состояние списка и действия

Состояния:

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

  1. Показывает список сообщений. Назовем состояние «сообщения» (массив).
  2. Показывает «Загрузка ..», если он находится в процессе получения сообщений. Назовем это состояние "загрузка" (логическое).
  3. Показывает «Ошибка», если есть ошибка. Назовем это состояние ошибкой (пустая информация или информация об ошибке).

Поскольку все вышеперечисленное относится к PostList, давайте поместим их в один объект состояния под названием postList.

{ postsList: {posts: [], error:null, loading: false} //initial state

Действия:

Этот компонент выполняет вызов AJAX для загрузки сообщений, поэтому мы воспользуемся указанным выше шаблоном и создадим 4 действия.

1. запрашивает у сервера список сообщений. Назовем это действие FETCH_POSTS.

export function fetchPosts() {
 const request = axios.get(`${ROOT_URL}/posts`);
return {
 type: FETCH_POSTS,
 payload: request
 };
}

2. Сообщает каждому компоненту, что он получил сообщения (успешный случай). Назовем это "FETCH_POSTS_SUCCESS"

export function fetchPostsSuccess(posts) {
 return {
 type: FETCH_POSTS_SUCCESS,
 payload: posts
 };
}

3. Сообщает каждому компоненту, что произошла ошибка (случай отказа). Назовем это «FETCH_POSTS_FAILURE».

export function fetchPostsFailure(error) {
 return {
 type: FETCH_POSTS_FAILURE,
 payload: error
 };
}

4. Сброс данных не требуется, потому что это первая страница (вы увидите, как это полезно на двух других страницах).

3.2 Компонент PostDetails - состояние списка и действия

Примечание: вы можете щелкнуть изображения, чтобы увеличить и прочитать

3.3 Компонент PostForm - состояние и действия

3.4 Компонент заголовка - состояние списка и действия

ШАГ 4. Создайте создателей действий для каждого действия

Всего у нас есть 12 действий (4 действия x 3 страницы), создайте создателей действий для каждого из них. Пожалуйста, смотрите исходный код здесь.

//Example Action creators...
export function fetchPosts() {
 const request = axios.get(`${ROOT_URL}/posts`);
return {
 type: FETCH_POSTS,
 payload: request
 };
}
export function fetchPostsSuccess(posts) {
 return {
 type: FETCH_POSTS_SUCCESS,
 payload: posts
 };
}
...

Термин Redux: «Редукторы»

Редукторы - это функции, которые берут «состояние» из объекта Redux и «действие» JSON и возвращают новое «состояние» для сохранения в Redux.

1. Функции редуктора вызываются контейнерами «Контейнер». когда есть действие пользователя или сервера.
2. Если редуктор изменяет состояние, Redux передает новое состояние каждому компоненту, а React повторно отображает каждый компонент

The below function takes the current "postsList" inside "...state" and merges new "postList" and creates a **new** state(json), if the action is "FECTH_POSTS_SUCCESS"
case FETCH_POSTS_SUCCESS:
 return { …state, postsList: {posts: action.payload, error:null,      
                              loading: false} 
        };

ШАГ 5 - Запишите редукторы для каждого действия

У нас есть 12 действий, нам нужно написать редукторы для каждого из них.

Пожалуйста, посмотрите исходный код для подробностей здесь.

Термин Redux: «Презентационные» и «Контейнерные» компоненты

Сохранение логики React и Redux внутри каждого компонента может сделать его беспорядочным, поэтому Redux рекомендует создавать фиктивный компонент только для презентации, называемый «презентационным» компонентом, и родительский компонент-оболочку, называемый компонентом «Контейнер», который работает с Redux, отправляет «Действия» и многое другое.

Затем родительский контейнер передает данные в презентационный компонент, обрабатывает события, обрабатывает React от имени презентационного компонента.

Легенда: желтые пунктирные линии = «презентационные» компоненты. Черные пунктирные линии = компоненты «Контейнер».

ШАГ 6 - Реализация каждого презентационного компонента

У нас есть 4 компонента: PostsList, PostDetails, PostForm и Header. Давайте создадим презентационные компоненты для каждого из них.

6.1 Реализация презентационного компонента - PostsList

Примечание: вы можете щелкнуть изображения, чтобы увеличить и прочитать

6.2 Реализация презентационного компонента - PostDetails

6.3 Реализация презентационного компонента - PostForm

Примечание. В реальном коде я использую замечательную библиотеку redux-form для проверки формы. Я напишу об этом в другом посте.

6.4 Реализация презентационного компонента - заголовок

Примечание: вы можете щелкнуть изображения, чтобы увеличить и прочитать

ШАГ 7 - Создайте компонент-контейнер для некоторых / всех презентационных компонентов

У нас есть 4 компонента: PostList, PostDetails, PostForm и Header. Давайте создадим компоненты контейнера для каждого из них.

7.1 Создание компонента контейнера - PostsListContainer

7.2 Создание компонента контейнера - PostDetailsContainer

7.3 Создание компонента контейнера - PostFormContainer

7.4 Создание компонента контейнера - HeaderContainer

ШАГ 8 - Наконец-то соберите их всех вместе

Код ниже - это упрощенная версия соединения всего вместе. Пожалуйста, просмотрите исходный код основных index.js и reducers.js, чтобы начать работу.

import React from 'react'; <-- Main React lib
import ReactDOM from 'react-dom'; <-- Main React DOM lib
import { Provider } from 'react-redux';<-- Injects Redux to comps
import { createStore, applyMiddleware } from 'redux';<- Redux
import { Router, browserHistory } from 'react-router';<- Navigation
import reducers from './reducers'; <- Import reducers
import promise from 'redux-promise';
//Configure middleware w/ redux-promise for AJAX requests
const createStoreWithMiddleware = applyMiddleware(
  promise
)(createStore);
const store = createStoreWithMiddleware(reducers);
ReactDOM.render(
  <Provider store={store}> <- Inject global redux state to comps
    <Router history={browserHistory}>
       <Route path=”/” component={App}> <- Wrapper for all pages
         <IndexRoute component={PostsIndex} /> <-wrapper Index page
         <Route path=”posts/new” component={PostsNew} /> <- New page
         <Route path=”posts/:id” component={PostsShow} /> <-Details
       </Route>
    </Router>
</Provider>
  , document.getElementById('body'));

На этом пока все!

Мои другие блоги

ПОСЛЕДНИЕ:

  1. [Видеокурс] Внутренняя работа браузера - для JavaScript и веб-разработчиков Используйте код: INNER15 и получите скидку 50%!

Функциональное программирование

  1. JavaScript является полным по Тьюрингу - объяснение
  2. Функциональное программирование на JS - с практическими примерами (часть 1)
  3. Функциональное программирование на JS - с практическими примерами (часть 2)
  4. Почему Redux нужны редукторы, чтобы они были« чистыми функциями »

ES6

  1. 5« плохих частей JavaScript, исправленных в ES6»

WebPack

  1. Webpack - запутанные части
  2. Замена Webpack и горячего модуля [HMR]
  3. HMR и React-Hot-Loader Webpack - Отсутствующее руководство

Draft.js

  1. Почему Draft.js и почему вы должны вносить свой вклад
  2. Как Draft.js представляет данные в формате RTF

Реагировать и Redux:

  1. Пошаговое руководство по созданию приложений React Redux
  2. Руководство по созданию приложения CRUD на React Redux (трехстраничное приложение)
  3. Использование промежуточного программного обеспечения в приложениях React Redux
  4. Добавление надежной проверки формы для реагирования на приложения Redux
  5. Защита приложений React Redux с помощью токенов JWT
  6. Обработка транзакционных писем в приложениях React Redux
  7. Анатомия приложения React Redux
  8. Почему Redux нужны редукторы, чтобы они были« чистыми функциями »

Salesforce

  1. Разработка приложений React Redux в Visualforce Salesforce

🎉🎉🎉 Если вам понравился этот пост, поделитесь им в Twitter. Https://twitter.com/rajaraodv 🎉🎉🎉

Спасибо за чтение !! 😀🙏