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. Откройте другую вкладку с тем же адресом и полюбуйтесь, как каждая страница обновляется, когда вы добавляете или удаляете задачи!
Спасибо за прочтение!