├── .gitignore
├── README.md
├── actions
├── PostActions.js
├── index.js
└── types.js
├── components
├── AddPost.js
├── Head.js
├── Loading.js
├── Nav.js
├── PostItem.js
└── PostList.js
├── lib
└── db.js
├── next.config.js
├── package.json
├── pages
└── index.js
├── reducers
└── PostReducer.js
├── static
└── favicon.ico
└── store.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | .idea
6 |
7 | # testing
8 | /coverage
9 |
10 | # production
11 | /build
12 | /dist
13 | /.next
14 |
15 | # misc
16 | .DS_Store
17 | .env
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 | /package-lock.json
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Table of Contents
2 |
3 | - [App Description](#app-description)
4 | - [Questions, Comments, Concerns](#questions-feedback)
5 | - [Dependencies and Inspiration](#dependencies-and-inspiration)
6 | - [Setting Up Cloud Firestore](#setting-up-cloud-firestore)
7 | - [Folder Structure](#folder-structure)
8 | - [Scripts](#scripts)
9 | - [npm run dev](#npm-run-dev)
10 | - [npm run build](#npm-run-build)
11 | - [npm run start](#npm-run-start)
12 | - [Setup and Running Example](#running-example)
13 | - [Live Demo](#live-demo)
14 |
15 | ## App Description
16 |
17 | This sample app demonstrates how to create a live list updated by user input in the provided status textbox using reactjs, next.js, redux, and cloud firestore (a firebase product).
18 |
19 | ## Questions, Comments, Concerns
20 |
21 | Please direct any questions, comments, or concerns to the [issues](https://github.com/NickDelfino/nextjs-with-redux-and-cloud-firestore/issues) section of the repo. Thanks for your feedback.
22 |
23 | ## Dependencies and Inspirations
24 |
25 | This project was seeded by [create-next-app](https://github.com/segmentio/create-next-app). For general questions about the setup of next.js and the structure visit the previously linked github.
26 |
27 | This project is inspired by the [examples](https://github.com/zeit/next.js/tree/master/examples) made by the nextjs team.
28 | It combines ideas from both their [with-redux](https://github.com/zeit/next.js/tree/master/examples/with-redux)
29 | and [with-firebase-authentication](https://github.com/zeit/next.js/tree/master/examples/with-firebase-authentication) examples
30 | to show how to create harmonization between redux and, in my case, cloud firestore.
31 |
32 | ## Folder Structure
33 |
34 | The project structure is made with going further in mind. Actions and reducers are moved
35 | into their own directories since there should be multiple of them in a full web app. A lib
36 | directory has been added for constant variable and to access the db.
37 |
38 |
39 | ```
40 | my-app/
41 | README.md
42 | package.json
43 | next.config.js
44 | components/
45 | Head.js
46 | Nav.js
47 | AddPost.js
48 | Loading.js
49 | PostItem.js
50 | PostList.js
51 | pages/
52 | index.js
53 | lib/
54 | const.js
55 | db.js
56 | static/
57 | favicon.ico
58 | reducers/
59 | postReducer.js
60 | actions/
61 | index.js
62 | types.js
63 | postActions.js
64 | store.js
65 | ```
66 |
67 | ## Setting Up Cloud Firestore
68 |
69 | Cloud firestore setup is similar to firebase realtime database setup.
70 |
71 | The main thing that is need are the initialization keys generated when making a database
72 | on the site so the example app works.
73 |
74 | Go to [firebase.google.com](https://firebase.google.com/). Login or create an account if
75 | you don't already have one.
76 |
77 | Then, go to console. Select the "Add Project" square.
78 |
79 | Name your project anything you like and then on the following page select the option
80 | "Add Firebase to your web app".
81 |
82 | A model will appear with configuration information. Copy the entire config variable and
83 | paste it over the placeholder one in ../lib/db.js.
84 |
85 | Then you are good to go. Your app will start using your Cloud Firestore.
86 |
87 | ## Setup and Running Example
88 |
89 | To run the project simply clone this repository and navigate into it.
90 |
91 | Run npm install to acquire dependencies.
92 |
93 | Then simply perform the command `npm run dev`.
94 |
95 | ## Scripts
96 |
97 | ### `npm run dev`
98 |
99 | Runs the app in the development mode at [http://localhost:3000](http://localhost:3000).
100 |
101 | ### `npm run build`
102 |
103 | Builds the app for production to the `.next` folder.
104 |
105 | ### `npm run start`
106 |
107 | Starts the application in production mode.
108 |
109 | This script is made with heroku in mind. There is a port variable that needs to be
110 | specified for it to run. Heroku needs this for deployment.
111 |
112 |
--------------------------------------------------------------------------------
/actions/PostActions.js:
--------------------------------------------------------------------------------
1 | import { loadDB } from '../lib/db';
2 | import {
3 | UPDATE_USER_POST,
4 | FETCH_POSTS,
5 | ADD_POST_SUCCESS,
6 | ADD_POST_FAILURE
7 | }
8 | from './types';
9 |
10 | //ACTIONS
11 | //This keeps the data in sync as the user types.
12 | //Also determines the length the user has left.
13 | export const updateUserPost = (userPost) => dispatch => {
14 | let newState = {
15 | userPost: userPost
16 | };
17 |
18 | dispatch({
19 | type: UPDATE_USER_POST,
20 | payload: newState
21 | });
22 | };
23 |
24 | //This adds the post to firebase cloudstore.
25 | //User typed data is reset upon success.
26 | //The failure action is fired but the reducer does not listen for it.
27 | //This is functionality that should be added in on a featured product.
28 | //For example, notify user the add failed and they should try again.
29 | export const addPost = (post) => async dispatch => {
30 | const db = await loadDB();
31 |
32 | db.firestore().collection('posts')
33 | .add({
34 | post: post,
35 | timestamp: db.firestore.FieldValue.serverTimestamp()
36 | })
37 | .then(() => {
38 | dispatch({
39 | type: ADD_POST_SUCCESS
40 | });
41 | })
42 | .catch((error) => {
43 | console.error("Error adding document: ", error);
44 | dispatch({
45 | type: ADD_POST_FAILURE
46 | });
47 | });
48 | };
49 |
50 | //This sets up the listener to fetch posts.
51 | //This pulls back an initial 50 posts but also sets
52 | //a listener so as new posts fill in their are added to the top.
53 | export const fetchPosts = () => async dispatch => {
54 | const db = await loadDB();
55 |
56 | db.firestore().collection('posts')
57 | .orderBy('timestamp', 'desc')
58 | .limit(50)
59 | .onSnapshot(snapshot => {
60 |
61 | let newState = {
62 | posts: []
63 | };
64 |
65 | snapshot.forEach(function(doc) {
66 | newState.posts.push({
67 | id: doc.id,
68 | post: doc.data().post
69 | });
70 | });
71 |
72 | dispatch({
73 | type: FETCH_POSTS,
74 | payload: newState
75 | })
76 | });
77 | };
--------------------------------------------------------------------------------
/actions/index.js:
--------------------------------------------------------------------------------
1 | export * from './PostActions';
2 |
--------------------------------------------------------------------------------
/actions/types.js:
--------------------------------------------------------------------------------
1 | //These are action types that can be broadcasted out and listened to.
2 | //Currently, ADDING_POST isn't being used by an actions or listened to.
3 | //ADD_POST_FAILURE is dispatched in the actions if adding a post fails but is not
4 | //being listened to in the reducer.
5 | export const FETCH_POSTS = 'FETCH_POSTS';
6 | export const ADD_POST_SUCCESS = 'ADD_POST_SUCCESS';
7 | export const ADD_POST_FAILURE = 'ADD_POST_FAILURE';
8 | export const ADDING_POST = 'ADDING_POST';
9 | export const UPDATE_USER_POST = 'UPDATE_USER_POST';
--------------------------------------------------------------------------------
/components/AddPost.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux'
3 | import { bindActionCreators } from 'redux'
4 | import { Button } from 'react-bootstrap'
5 | import TextareaAutosize from 'react-autosize-textarea';
6 | import { updateUserPost, addPost } from '../actions'
7 |
8 | class AddPost extends React.Component {
9 | //When user clicks add post fire the add post event.
10 | onClick(){
11 | this.props.addPost(this.props.userPost);
12 | }
13 |
14 | //Update user text input as they type by firing this action.
15 | handleChange(event){
16 | this.props.updateUserPost(event.target.value);
17 | }
18 |
19 | render() {
20 | const { userPost } = this.props;
21 |
22 | return(
23 |
7 | {post.post} 8 |
9 | 29 |