Создание одностраничного приложения 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. Показывает список сообщений, 2. Позволяет перейти на страницу формы сообщения для создания нового сообщения. В итоге мы получаем компоненты PostsList и Header.
- Страница сведений о публикации: 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}}
Действия:
- 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 - Состояние списка и действия
Состояния:
Перечислите различные данные, которые могут изменить отображение компонента на всех этапах работы компонента.
- Показывает список сообщений. Назовем состояние «сообщения» (массив).
- Показывает «Загрузка ..», если он находится в процессе получения сообщений. Назовем это состояние "загрузка" (логическое).
- Показывает «Ошибка», если есть ошибка. Назовем это состояние ошибкой (пустая информация или информация об ошибке).
Поскольку все вышеперечисленное относится к 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'));
На этом пока все!
Мои другие блоги
ПОСЛЕДНИЕ:
- [Видеокурс] Внутренняя работа браузера - для JavaScript и веб-разработчиков Используйте код: INNER15 и получите скидку 50%!
Функциональное программирование
- JavaScript является полным по Тьюрингу - объяснение
- Функциональное программирование на JS - с практическими примерами (часть 1)
- Функциональное программирование на JS - с практическими примерами (часть 2)
- Почему Redux нужны редукторы, чтобы они были« чистыми функциями »
ES6
- 5« плохих частей JavaScript, исправленных в ES6»
WebPack
- Webpack - запутанные части
- Замена Webpack и горячего модуля [HMR]
- HMR и React-Hot-Loader Webpack - Отсутствующее руководство
Draft.js
Реагировать и Redux:
- Пошаговое руководство по созданию приложений React Redux
- Руководство по созданию приложения CRUD на React Redux (трехстраничное приложение)
- Использование промежуточного программного обеспечения в приложениях React Redux
- Добавление надежной проверки формы для реагирования на приложения Redux
- Защита приложений React Redux с помощью токенов JWT
- Обработка транзакционных писем в приложениях React Redux
- Анатомия приложения React Redux
- Почему Redux нужны редукторы, чтобы они были« чистыми функциями »
Salesforce
🎉🎉🎉 Если вам понравился этот пост, поделитесь им в Twitter. Https://twitter.com/rajaraodv 🎉🎉🎉
Спасибо за чтение !! 😀🙏