Щелкните здесь, чтобы опубликовать эту статью в LinkedIn »

Firebase - это потрясающая база данных в реальном времени, созданная людьми из Google. Он позволяет сохранять данные, не полагаясь на какую-либо базу данных на стороне сервера, и обновляется в режиме реального времени! В этом уроке я покажу вам, как использовать его с React-Redux при создании приложения ToDo. Да я знаю, я очень оригинален. Не стесняйтесь загрузить готовое репо по адресу https://github.com/wickard/redux-firebase-tutorial.

Для начала позвольте обмануть и получить помощь от наших друзей в Facebook. Npm установить приложение create-response-app. Create-react-app - отличный инструмент, который поможет вам быстро запустить проект React. Идите вперед и запустите create-response-app * ваше имя проекта * в своем терминале, чтобы создать новую папку проекта. Cd в свой проект и запустите команду npm install. Npm start запустит ваш проект, найдите минутку, чтобы насладиться вращающимся логотипом React.

Добро пожаловать назад! Давайте добавим еще несколько требований, прежде чем мы начнем. В консоли введите следующее: npm install firebase redux react-redux redux-thunk. Когда наша среда готова к работе, пора настроить нашу базу. Перейдите на https://firebase.google.com/ и создайте аккаунт / проект. Вернувшись в свой проект, давайте создадим наш файл firebase в папке src. Я называю свой firebase.js. Вверху файла импортируйте firebase так:

import firebase from ‘firebase’

Снова в Интернет! На домашней консоли firebase нажмите кнопку «добавить firebase в ваше веб-приложение». Результат должен выглядеть примерно так.

<script src="https://www.gstatic.com/firebasejs/4.11.0/firebase.js"></script>
<script>
  // Initialize Firebase
  var config = {
    apiKey: "YOUR API KEY",
    authDomain: "YOUR DOMAIN",
    databaseURL: "YOUR DATABASE URL",
    projectId: "etc",
    storageBucket: "etc",
    messagingSenderId: "etc"
  };
  firebase.initializeApp(config);
</script>

Давайте избавимся от тех тегов скрипта, которые нам не понадобятся. Скопируйте все, что находится между ними, и давайте инициализируем нашу базу данных. Ваш файл firebase.js должен выглядеть примерно так:

import firebase from ‘firebase’
import uuid from 'uuid/v4'
const config = {
authDomain: “YOUR DOMAIN”,
databaseURL: “YOUR URL",
projectId: “YOUR ID”,
storageBucket: “YOUR BUCKET”,
messagingSenderId: “YOUR ID”
};
firebase.initializeApp(config);
const database = firebase.database()
export default database

Теперь нам просто нужна пара функций, которые будут добавлять и удалять задачи. Выше мы также импортировали библиотеку uuid, доступную с помощью node. Мы будем использовать его, чтобы присвоить нашим задачам уникальные идентификаторы.

export const addTaskToFirebase = (task) => {
 const id = uuid()
 database.ref(`/${id}`).set({
 task, id
 })
}
export const removeTaskFromFirebase = (id) => {
database.ref(`/${id}`).remove()
}

Чтобы добавить что-то в нашу недавно созданную базу firebase, мы добавим дочерний элемент в корень нашей базы данных с уникальным идентификатором, который мы создаем. Чтобы удалить, мы просто вызываем remove на том же маршруте. Лучшая документация, чем я могу предоставить, живет здесь: https://firebase.google.com/docs/web/setup.

Все в порядке! Наша база наконец создана. Давайте добавим задачи в наш список. Мы можем создать простую форму для добавления элементов в список. Кнопка отправки подтолкнет задачи к нашей базе данных. Перейдите в файл App.js и добавьте следующее:

import {addTaskToFirebase, removeTaskFromFirebase}  from './firebase'
export default class App extends Component {
render() {
return (
<div>
  <div className="App">
  <header className="App-header">
    <img src={logo} className="App-logo" alt="logo" />
    <h1 className="App-title">Welcome to React</h1>
  </header>
  <p className="App-intro">
  To get started, edit <code>src/App.js</code> and save to reload.                 </p> 
 </div>
 <div>
   <form onSubmit={(e) => {
     e.preventDefault()
     addTaskToFirebase(e.target.task.value)}}>
     <input type="text" name="task" />
     <input type="submit" name="add task" />
   </form>
 </div>
  <div>
    <h2> Todo:</h2>
    <ul>
      we will map over our store later!
    </ul>
  </div>
</div>
  );
 }
}

Большой!

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

Теперь мы запускаем npm start из панели задач и можем сортировать добавление задач в наш список дел. Ничего не происходит после того, как мы нажимаем добавить задачу на странице. Но если мы вернемся на вкладку базы данных в вашем проекте firebase, добавляемые вами задачи должны отображаться в вашей базе данных в реальном времени.

Это здорово и все такое, но мы, вероятно, хотели бы, чтобы они отображались на странице. В магазин! В этом руководстве я собираюсь осветить детали реализации хранилища redux, если вы хотите получить дополнительную информацию, посетите https://egghead.io/courses/getting-started-with-redux. Создайте новый файл в каталоге src store.js. Вверху мы будем обрабатывать наш импорт:

import { createStore, applyMiddleware } from 'redux'
import database from './firebase'
import thunkMiddleware from 'redux-thunk'

Сначала мы сделаем действие, чтобы получить все наши задачи из firebase. Код для этого будет выглядеть примерно так.

/**
* ACTION TYPES
*/
const GET_TASKS = 'get tasks'
/**
* ACTION CREATORS
*/
export const getTasks = (tasks) => ({type: GET_TASKS, tasks})
/**
* THUNKS
*/
export function getTasksThunk() {
 return dispatch => {
 const tasks = [];
 database.ref(`/`).once('value', snap => {
  snap.forEach(data => {
  let task = data.val();
  tasks.push(task)
  })
 })
 .then(() => dispatch(getTasks(tasks)))
 }
}

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

/**
* REDUCER
*/
function Reducer (state = [], action) {
 switch (action.type) {
  case GET_TASKS:
   return action.tasks
  default:
   return state
  }
}
export default createStore(Reducer, applyMiddleware(thunkMiddleware))

Последняя строка создает наш магазин с нашим редуктором. Мы экспортируем его, чтобы использовать с остальной частью нашего приложения. Откройте наш основной индексный файл и добавьте в него несколько строк, чтобы импортировать Provider из react-redux. Мы используем провайдера, чтобы предоставить всем нашим компонентам доступ к нашему хранилищу redux (и нашей базе firebase через него).

...
import store from './store'
import { Provider } from 'react-redux'
ReactDOM.render(
 <Provider store={store}>
  <App />
 </Provider>,
 document.getElementById('root'));
...

Снова откройте компонент App и давайте подключим наш магазин. Импортируем магазин и наш getTaskThunk вверху файла. Мы будем использовать функцию подключения, чтобы хорошо… подключить наше приложение к магазину. Более подробную информацию о подключении можно найти в этой замечательной статье здесь https://medium.com/mofed/reduxs-mysterious-connect-function-526efe1122e4.

import { connect } from 'react-redux'
import { getTasksThunk } from './store'

В jsx компонента добавьте эту строку для отображения всех наших задач.

...
<div>
 <h2> Todo:</h2>
 <ul>
  {this.props.tasks.map(item => <li key={item.id}>{item.task}<button
  onClick={() => removeTaskFromFirebase(item.id)}>x</button></li>)}  </ul>
</div>
...

Откуда берутся эти задачи? Рад, что вы попросили, чтобы мы получили их из магазина через функцию подключения, это выглядит примерно так:

const mapState = state => ({
 tasks: state
 })
const mapDispatch = dispatch => {
 dispatch(getTasksThunk())
 return {
 }
}
export default connect(mapState, mapDispatch)(App);

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

Запустите еще один npm start и wallah! Должны появиться задачи, которые вы ранее добавили в свою базу данных. Попробуйте добавить еще один. Ничего ›.›, Но при обновлении отображается, как и ожидалось. Давайте исправим это с помощью изящных слушателей событий, предоставленных нам firebase.

Откройте файл store.js и добавьте следующее:

/**
* ACTION TYPES
*/
...
const ADD_TASK = 'add task'
const REMOVE_TASK = 'remove task'
/**
* ACTION CREATORS
*/
...
export const addTask = (task) => ({type: ADD_TASK, task})
export const removeTask = (task) => ({type: REMOVE_TASK, task})
/**
* LISTENERS
*/
export function watchTaskAddedEvent(dispatch) {
 database.ref(`/`).on('child_added', (snap) => {    dispatch(addTask(snap.val()));
 });
}
export function watchTaskRemovedEvent(dispatch) {
 database.ref(`/`).on('child_removed', (snap) => {
 dispatch(removeTask(snap.val()));
 });
}

Firebase позволяет вашему приложению прослушивать события и выполнять обратные вызовы при возникновении события. Полный список слушателей можно найти в документации здесь https://firebase.google.com/docs/database/admin/retrieve-data. Мы будем использовать события child_added и child_removed. Каждый раз, когда элемент добавляется в нашу базу данных, он отправляет функцию addTask с данными, которые только что получила база данных реального времени. Аналогичным образом он будет отправлять removeTask всякий раз, когда элемент будет удален.

Обновите редуктор для обработки этих новых действий:

/**
* REDUCER
*/
function Reducer (state = [], action) {
 switch (action.type) {
  case GET_TASKS:
   return action.tasks
  case ADD_TASK:
   return [...state, action.task]
  case REMOVE_TASK:
   return state.filter(task => task.id !== action.task.id)
  default:
   return state
 }
}

Все, что осталось, чтобы подключить наших новых слушателей к компоненту приложения. Откройте App.js и добавьте две новые функции import.

import { getTasksThunk, watchTaskAddedEvent, watchTaskRemovedEvent } from './store'

Добавьте их в свой mapDispatch, и все готово!

const mapDispatch = dispatch => {
 dispatch(getTasksThunk())
 watchTaskAddedEvent(dispatch)
 watchTaskRemovedEvent(dispatch)
 return {
  }
}

Запустите npm start еще раз и бац! Наше приложение добавляет и удаляет задачи по команде. Но вы могли сделать это до firebase. Откройте другую вкладку с тем же адресом и полюбуйтесь, как каждая страница обновляется, когда вы добавляете или удаляете задачи!

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