- Simple full-stack app that shows a list of images from the React client and processed on the Express server. Image filename and filepath are saved in a hosted Mongo Atlas Database, while the images themselves are saved in Cloudinary, a cloud provider for hosting images and video. The two main things I learned from this app is how to use React for image upload and image preview.
-
A simple Instagram-like clone using React for the frontend client, Express node.js for the backend server, using MongoDB Atlas as the cloud hosted database, and Cloudinary for the cloud based image hosting. This was built to better understand image upload with React. I say Instagram-like because it's one of the most popular image app. This is more for getting practical application of image/file uploading using React and React hooks.
-
React is a separate frontend server, which is different than a view engine like EJS, PUG, or JADE. Using a view engine with a backend server simplifies image uploading. However, I wanted to see how to pass the "app state" of images and an authenticated user across the application to different pages. The app was built by searching several tutorials, MDN Docs, and googling Stackoverflow answers.
- Node.js
- Express server
- MongoDB Atlas for cloud hosted database
- Cloudinary for cloud based image storage
- Multer middleware used for uploading files
- Multer Storage Cloudinary library that assists multer in uploading to Cloudinary.
- React UI for frontend user interface
- JSON Web Tokens for authentication
- Bcryptjs for hashing
- Express Validator additional layer of validation
- Uncontrolled Components For React file inputs
- FormData MDN documentation on Form Data
-
Normally, the setup would be to clone this repo, change directory into it, then type
npm install
andnpm client-install
. Because of the services and environment variables for JWT secret, Cloudinary, Mongo Atlas, etc. you would need to open your own account, set up those services, and set your own secrets and environment variables. The environment variables that need to be set are MONGO_ATLAS_SECRET, JWT_SECRET, CLOUDINARY_CLOUD_NAME, CLOUDINARY_KEY, and CLOUDINARY_SECRET. -
After the addition of new features and resolving some Update issues, this will be deployed on Heroku. When that happens, links to the app will be made available.
- Image upload
- Image storage, retrieval, and deletion.
- User login and logout
- Alerts for invalid user register and login
- Images are displayed by time in ascending order (most recent first).
- Eliminate image flicker when writing the description on image upload form
- Better handling of asynchronous action from image upload, utilizing a spinner, and redirect/refresh to images list.
- Enable Edit Image
- Include profile page
- Image upload for user profile image
- UI refactor
- Enable Likes, Comments, and Replies in Comments
- Threading for replies
-
Uploading images with React is different than with a HTML form. In HTML forms you need to add a property
enctype
and set it tomulti-part/form-data
. In a React form that is not needed (that would be done in a fetch or axios request header settingContent-type
tomultii-part/form-data
). In the form, the input type is set to file. The onChange function (or whatever change handler function is used) needs to take ine.target.files[0]
when uploading one file or image. The reason ise.target.files[0]
is an array, so the target is the first element of the files array.const changeHandler = (e) => {
setFile(e.target.files[0]);
};
Note that you cannot use the same onChange function for files AND user inputs. The reason is that user inputs are controlled inputs, meaing React controls them. File inputs are UNCONTROLLED COMPONENTS, meaning that the DOM controls the inputs. If the same onChange function is used, React will throw an error saying that both controlled and uncontrolled inputs cannot be in the same form. -
The file attached to a FormData object and is sent via an onSubmit function. Working with the files and the FormData object can be tricky, since you can't attach or call a property in the object. FormData requires methods to get and set data: get(), append(), set(), etc., to access key value pairs. This can cause confusion since the usual pattern is to call the desired property.
-
Working with images and associated data using React, Context, and Express was more difficult than I initially thought. Some of those issues are captured in the deep dive and pending features. The basic issue is working with React, synthetic events, and images is different than working with HTML forms and images. Many small issues arise due to the difference between a React uncontrolled component (image upload) and React controlled component (user input). Then sending the Form Data which includes the image and associated text through Context to the Express server, manipulating the data, and back to the React UI. Retrieving and manipulating that Form Data didn't work as working with JSON, but it is a learning experience. This is what I have learned so far and my intention is to learn more and update this app in the future.
One example is even if the app is about creating images, don't call a post an image. Confusion arises between the different data fields like description and name of the post that is called image and the image (file) itself.
Another issue was the flickering of the image preview while entering description.
Another example is that if the axios req is structured as:
axios.post({ req })
instead of axios({ method: "post", data...})
the file would be included in the http address as if it was sending JSON instead of a data object in form data. The documentation is not clear on the reason (neither in axios, MDN Form Data, Multer, etc), so it would appear to be a setting in a version of one of the libraries. As updates occur to the libraries and npm packages, I will update the app.
- Multer. The Multer middleware attaches a file object to the request object. Working in conjunction with Multer Cloudinary Storage, the request object is the filename and file path to the image in Cloudinary. Also note that Multer is synchronous and doesn't support Promises or asynchronous actions. So, that needs to kept in mind when writing the middleware in the routes.
- The inspiration for this app trying to add image upload to another app (Barker), getting practice with React hooks to manage state instead of redux, using context to manage app state, specifically with image uploading and image rendering. Also, drew inspiration from the various image applications like Instagram, 500px, Flickr, and Unsplash.
- 500px
- Unsplash
- Flickr
- Frontend Masters: Complete Intro to React V6: hooks, effects, contexts, etc
- Dan Abramov: Fundamentals of Redux
- repo created by Don Spire Nspired1, email: don.spire1@gmail.com