A community for writers to release creativity and also find inspiration.
- React JS (create-react-app)
- Firebase Auth / Firestore / Storage
- Jest
- functional components and hooks
- custom hooks
- form validation
- authorisation with Firebase
- CRUD operations using Firebase Firestore
- private routes based on authorised user
- dynamic routes for profiles, posts
- comments on posts
- react portal for modals (added before body close) with element being removed from DOM on unmount
- modal focus trap to stop users tabbing around elements behind
- modal event listener for escape key press to cancel and close
- modal form validation to delete posts if typed value matches string
yarn install
yarn start
I switched to bootstrapping the project with create-react-app due to issues deploying to Vercel.
I didn't quite know where I was going to take this, I just built it as I went which is usually the case with my personal projects. This means that unfortunately I did not have a set design, layout, structure or idea of components I would need ahead of time. This leads to the need to refactor quite a lot but I feel it helps exercise my mind.
So I was looking to pick up Firebase using their JavaScript SDK while I was also learning React's Context functionality as this has been something I have put off for some time.
When approaching this project I thought I'd start with a blank canvas, no use of create-react-app, just completely from scratch.
I really wasn't sure how this would go.
So first I checked the Create a New React App section on the React website.
One thing it states that I will need is the following:
- Node version of 14.0.0+
- npm version of 5.6+
I already have these installed so I will move on to the next piece of the puzzle.
The main requirements area to have a package manager, a bundler and compiler. For this I have chosen to use:
The React documentation states that the npm version must be 5.6 or above. I chose npm since I already have the latest stable version installed.
I chose Webpack as the bundler since it is the most popular and seems to be used with create-react-app
, so I feel a bit more comfortable using it. I feel like there will be a lot more support out there for this, rather than something such as Parcel.
I opted to use Babel as the compiler as this is the only one that I have ever used and am aware of.
The React documentation stops here and recommends an article on Medium for setting up a toolchain from scratch
So to start off, once initialising npm and git, the article recommended creating a public
and src
folder, as we all know, the staples of the React folder structure.
It mentions that we should think ahead regarding git so we will set up a .gitignore
file which excludes the following folders:
- node_modules
- dist
The public
folder will be used to handle the static assets (favicon, index.html etc). On top of this, I will also be creating folders within the src directory for components
, context
and lib
. The context
folder will of course hold context such as the user authentication state andThe lib
folder will hold any libraries or development kits in use such as Firebase
.
First I have installed Babel using npm install --save-dev @babel/[email protected] @babel/[email protected] @babel/[email protected] @babel/[email protected]
after looking up the latest versions.
The article gives some information on the packages, explaining babel-core is the main Babel package, allowing it to make transformations to the code for optimal browser compatibility. The CLI will allow compiling from the command line which will be handy when we set up the scripts to run the build or the development server. The presets will transform the code from ES6+ and JSX in to ES5.
Once done, I will add a .babelrc
file which will hold an object that will tell babel which presets we'd like to use. A full list of Babel's plugins can be found here - I may take a look at these at a later date, but for now I just want the core fundamentals in place.
Webpack's website states that you do not need to use a configuration file when using Webpack however it looks like we will need configuration to set some rules in the development mode, the entry point (which will be the index.js
we will create in the src folder) and the location of the dist folder to build in to.
I have installed Webpack using npm install --save-dev webpack webpack-cli webpack-dev-server style-loader css-loader babel-loader
. I've removed the version's given in the Medium article but I'm not sure if this will cause any issues further down the line. Webpack is now in version 5 wheras the article is recommending version 4. Hopefully there will be no issues with any other configuration to come, but we'll see. This article was last edited in 2018 but I'm hopeful.
The article states that 'Webpack uses the loaders to process different types of files for bundling.' It will also serve our React project in development and allow any saved changes to be reflected immediately in the browser with an automatic reload. A webpack.config.js
file was created with some code to make this possible.
The article has not mentioned installing post-css for CSS prefixers to be added in the output so I have taken it upon myself to do this. First I have installed the required packages using npm install --save-dev postcss-loader postcss
as instructed on the Webpack postcss loader page
It turns out I also need npm install postcss-preset-env
which is then added as a plugin in the postcssOptions
array within the webpack.config.js
file. This can also be done in its own file as a module but I think I will keep everything in one place.
Now everything is (hopefully) in place, I will install react
and react-dom
.
Once this is complete I'll need to set up my index.js within the src directory since we have set this as the entry point within Webpack's configuration.
I will use ReactDOM.render
as this is the main function that tells React what to render and where (in this case we will use the element with the ID "root" which you can find in the public/index.html
file on line 10)
After this, we just need to create the App.js
file to hold the App component
which is rendered in the index.js
file and we should be set to install any extra libraries we may want to use.
So I would like to think about any CSS Frameworks, libraries I'd like to use etc. I will install the following:
This will provide me with SVG icons in the form of pre-made components. There are so many to choose from and I just love this library. It's so easy to use
This is an excellent CSS Framework to get some sort of skeleton/base in place. I would like to use TailwindCSS
but I am not too familiar with it so I will pass for now. For this project I want to focus on learning to use Firebase
and Context
.
This is a necessary library which allows me to place titles on each page. All that we need to do is wrap the project in the Helmet.Provider context and then we can begin to use the Helmet component to add dynamic titles to our app.
I will be using React Router Dom to set up dynamic routes in my app. It will allow me to re-direct to the correct places depending on the user authentication response from Firebase
. I will also need it to route the user to pages such as the sign up and register pages.
I don't really have an idea set in stone. I'm going to go in to this first getting Firebase
implemented. Then I will set down a base within the App.js
file. Once this is done I will look in to implementing Context
to store the user auth
data.
Folder structure is one thing I like to think about but I never know what would be best. I am going to go with the usual:
- assets (for images, videos, any data such as JSON files I may want to include)
-- videos
-- images
-- data
- components (this will hold all of the stateless components such as Nav, Header etc)
-- Header
-- Nav
- containers (this will hold our stateful components such as the pages like Home, SignUp etc)
-- Home
-- SignUp
-- Register
- context
-- AuthContext
- lib
-- firebase
I used to name the files after its parent folder within the components
file, for example: components/Header/Header.js
but lately I prefer to use the following structure: components/Header/index.js
. This will allow me to import quicker i.e. import Header from ./components/Header
instead of ./components/Header/Header
This brings another thing to my attention - the base path for importing files. As I get deeper in to folders, it is easy to get confused with how many levels are required to go back to reach the correct contents, even if the code editor displays the list of files as you type. For this I am going to use the following within Webpack's configuration file.
I always forget how to do this so I've taken a look around and found another article on medium titled Getting rid of relative paths in imports using webpack alisa
This seems really useful. I will update the webpack config with the following:
resolve: {
alias: {
components: path.resolve(__dirname, ‘src/components/’),
containers: path.resolve(__dirname, ‘src/containers/’),
assets: path.resolve(__dirname, ‘src/assets/’),
context: path.resolve(__dirname, ‘src/context/’),
lib: path.resolve(__direname, 'src/lib'),
}
}
This will now allow me, for example, to import a component using import Header from "components/Header"
instead of import Header from "../../../components/Header"
etc
That should be everything in place for now, hopefully!
Edit: It looks like I forgot some vital steps.
I will need to use something called react-hot-loader
in order for changes to reflect in the client. Apparently this is called "Hot Module Replacement". This was provided by the React team here.
I will also need to add some scripts to the package.json
in order to run the development server and create builds. To do this I will add the following:
"scripts": {
"start": "webpack-dev-server --config ./webpack.config.js",
"build": "webpack --mode production",
"test": "echo \"Error: no test specified\" && exit 1",
},
So after completing all of the above, I'm running in to an error when running the start script - SyntaxError: Cannot use import statement outside a module
So it looks like I've changed the path and webpack variables in the webpack config to import statements which can not be used outside of a module, silly me! I have updated this and hopefully things will work.
[webpack-cli] Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
- options has an unknown property 'hotOnly'. These properties are valid:
object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, static?, watchFiles?, webSocketServer? }
Okay so upon investigation this, it seems that the latest version of Webpack (5) does not support the property hotOnly
within the config under the devServer
property. I have updated this to hot: "only"
which should work according to stack overflow.
[webpack-cli] Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
- options has an unknown property 'publicPath'. These properties are valid:
object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, static?, watchFiles?, webSocketServer? }
I was unable to find anything, I will come back to this. I have updated the publicPath
key to host
as this is an accepted property. It has worked for now
[webpack-cli] Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
- options has an unknown property 'contentBase'. These properties are valid:
object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, static?, watchFiles?, webSocketServer? }
I'm seeing a trend. It appears the article I followed would work should I have installed the versions that were mentioned, however I have decided to install the latest Webpack which is causing issues. I may change the version but for now, I would like to see what I can do.
Upon searching around a bit I have found this thread which explains that the documentation for webpack-dev-server is out of date and that contentBase
was replaced with static
This error was resolved with the static
option mentioned above.
The only issue I am having now is that I can not see changes when I save files, I need to reload manually. The react-hot-loader was installed for this reason, so I believe there is something wrong here.
Edit: This was fixed by changing the following webpack.config.js
property and value: devServer.hot: "only"
to devServer.hot: true
So with create-react-app
you no longer need to import React from 'react'
when creating components. I have found this article explaining why. I have yet to read it all but I will update once I have. I am hoping there is a way that I will be able to implement it within this project, having not used create-react-app
Edit: The article doesn't seem to hold anything of value but I have found the article Introducing The New JSX Transform on React's website. This holds the key to upgrading the project. It seems we just need to update the .babelrc file to provide a few further options to the @babel/preset-react
plugin as follows:
{
"plugins": ["react-hot-loader/babel"],
"presets": [
["@babel/env"],
["@babel/preset-react", {
"runtime": "automatic"
}]
]
}
So I've had a lot of issues implementing Firebase. Mostly with setting the displayName of the user and rendering it on the dashboard page. I was receiving an error message each time I tried to use async/await syntax which said:
Uncaught ReferenceError: regeneratorRuntime is not defined in React
After searching around a bit, it turns out this is happening as I did not have @babel/plugin-transform-runtime installed.
So the reason this plugin is required is because Async functions
are abstraction on top of generators. While they're supported in the latest versions of Node (10+), when using Babel we need to add an extra layer that transforms generators to change ES6 in to ES5 at runtime. This is because their syntax is not backwards compatible.
I also had some issues with react-router-dom
- I hadn't realised that a new major version was out. It no longer uses things like <Redirect />
or <Switch />
but instead uses useNavigation
, <Navigation />
, and <Routes />
instead. I have used the docs to find all the current exports.