This is a solution to the #1 Project out of the 7 React Projects for Beginners in 2023. The carousel should allow the user to click the backwards or forwards button to go to the previous or next image.
- complete the remaining sections
- put an access link to the
sol
branch, so viewer can refer to it when reading the documentation - indenting the documentation correctly
- correct spelling mistakes and phrases construction
- remove this section
- Overview
- Installation
- Project building process
- Solutions comparison
- What I learned
- Continued development
- Useful resources
- Author
- Acknowledgments
s-t-e-v (challenger)
- Solution URL: Github repo
- Live Site URL: Add live site URL here
Reed Barger @freecodecamp.org (project poster)
- Solution URL: Code sandbox
If you want to run my solution on your pc, follow the instructions:
-
Open your terminal and then type
git clone https://github.com/s-t-e-v/carousel-react
-
cd
into the new folder and typenpm install
-
To run the React project
npm start
Credits: Sattwik Sahu for explaing how to download & run react project locally here.
- Semantic HTML5 markup
- CSS custom properties
- Flexbox
- React - JS library
- The images will be stored in a simple array.
use state
should be used in order to store the current image. Updating the state will trigger going to the previous or next image, according to the button the user pressed.- If the user has gone through all of the images, it should be allowed to cycle through the images again when clicking to the next/previous image buttons.
- Concepts that should be implemented:
- useState (storing and updating state)
- Conditionals (ternaries)
- Lists, keys, and .map()
- Applying
export default
to a function calledApp
:
export default function App() {
return (
{/* code here */}
);
}
- Using an array to store image path information :
const images = [
"path 1",
"path 2",
"etc...",
];
- Assigning to the left & right buttons of the carousel a class specific to both of them. E.g.:
{<element className = "left-arrow"></element>}
- Applying to
onClick
a function handling click event for the left & right buttons - Importing
useState
:
import { useState } from "react";
- Setting up the state of the carousel, being the index of the current image here:
export default function App() {
const [current, setCurrent] = useState(0);
// ...
}
General observations: Both of the solutions ...
- CSS stylesheets in my solution is imported in "index.js":
index.js
// Some imports here...
import './style.css';
// rest of the file...
In the project poster solution, the stylesheet is imported in "App.js":
App.js
import { useState } from "react";
import "./styles.css";
// ...
-
The array of image paths is declared outside of the
App
function in the project poster solution, whereas declared inside theApp
function in my solution. -
In term of App structure,
- the project poster has only one functional component:
App
. - In my solution, the
App
component is composed of a several sub functional components like<Carousel/>
,<Arrowleft>
,<Arrowright>
. My solution is more 'decomposed'.
- the project poster has only one functional component:
-
My solution is more verbose than the project poster solution:
- My solution: 68 lines of code
- Project poster solution: 44 lines of code
-
Click events handling:
- My solution uses on function to handle click events. To manage clicks from the right & left buttons, I use
LEFT
andRIGHT
constants and pass them as arguments through theside
variable:
function handleClick(img, side) { // code here... }
- The project poster uses two functions to handle click events,
prevSlide()
andnextSlide()
, for handling clicks of the left and right buttons respectively.
- My solution uses on function to handle click events. To manage clicks from the right & left buttons, I use
-
Click events handling - image index:
- My solution passes
currentImg
value for theimg
argument. An anonymous function is used for this purpose:
function Carousel() { // currentImg useState & const images definition function handleClick(img, side) { // code here... } return ( <div className="carousel"> <Arrowleft onLeftClick={() => handleClick(currentImg, LEFT)}/> {/* rest of div content here...*/} </div> ); }
Besides, as we can see in the code above, the
handleClick
function is passed to the arrow components viaonLeftClick
andonRightClick
props.- The project poster directly uses currentImg value with
handleClick
function, treating it as global variable. No need ofLEFT
orRIGHT
constants since right and left buttons have their own click handlers. Hence, thehandleClick
functions here does not require any arguments:
function nextSlide() { // code using 'current' (aka current image index) directly }
- My solution passes
-
Click events handling - onClick trigger:
- My solution implements onClick event listenning within the arrow functional components. The event listening function is a props passed to the component. E.g.:
function Arrowleft({onLeftClick}) { return ( <button className="arrow arrowleft" onClick={onLeftClick}></button> ); }
- The project poster implements onClick event listenning directly in the arrow components, hence no need of passing a event handling function through props. E.g.:
{<div className="left-arrow" onClick={prevSlide}> ⬅ </div>}
-
Click events handling - algorithm:
- My solution: I decided to use
switch
to handle the left and right cases. For each cases, I use if statements to increase or decrease the index of the imagesimg
. When the index is out of bounds, we set it to the upper or lower bound. This create a 'loop'. Then, I call thesetCurrentImg
state function to update the current image index. E.g.:
function handleClick(img, side) { switch(side) { case LEFT: if(img > 0) img--; else img = images.length - 1; break; // RIGHT & default cases... } setCurrentImg(img); }
- Project poster solution: The left and right cases are handled in two separate functions.
Ternary expressions are used to manage the index increase/decrease looping.
setCurrent
state function is directly called, with the ternary expression as argument. E.g.:
function prevSlide() { setCurrent(current === 0 ? images.length - 1 : current - 1); }
- My solution: I decided to use
-
HTML:
- My solution: use of
<button>
tag for the left and right arrows. - Project poster solution: use of
<div>
tag for the left and right arrows.
- My solution: use of
-
Image rerendering:
- My solution: The slide image is contained in a div. The image source is set by the value of
images[currentImg]
:since{<img src={require("./img/" + images[currentImg])} alt=""/>}
currentImg
changes when an arrow is clicked, the corresponding image file name contained inimages
array is returned. Hence, the right image is loaded. - Project poster solution: . The image is contain in a div like previously. The image supposed to be shown is via an iteration through the
images
array using the.map()
method:
jsx {images.map( (image, index) => current === index && ( <div key={image} className="slide"> <img src={image} alt="images" /> </div> ) )}
Here, the short-circuit evaluation
current === index && (Image JSX)
is used to determine whether the image of the iteration should be shown or not. At the end, the array returned by the.map()
method contains only one element: the image JSX of the current image index. The 'key' property of the div is needed to help react to discriminate the different div image containers so it can rerender the app correctly. - My solution: The slide image is contained in a div. The image source is set by the value of
My solution Pros:
- x
- y
- z
Cons:
- a
- b
- c
Project poster solution Pros:
- x
- y
- z
Cons:
- a
- b
- c
- I learned how to set up a react project:
npm init react-app my-app
- I learned the minimal structure of a React application:
project-name
node_modules
public
favicon.ico
index.html
manifest.json
src
App.js
index.js
style.css
package-lock.json
package.json
- According to the compiler, a
switch
statement should always has a default case statement. - In JSX notation, (and maybe in Js overall),
require
statement is needed to source an image:
jsx
{<img src={require("image_path")} alt="" />}
also, even if alt
is empty, we must put it the image tag.
- I learned how to pass function from a parent component to a child component via props
- I learned how to use anonymous function to pass arguments to function used in child component
- I learnd using props
- I learned how to make the app dynamic thanks to state variables.
- Fragment element of the UI into functional components
- How to establish relationship between parent and child components, so change induce by child component affect the parent state
- It makes more sense to define
images
array outside of the main functionApp
. - It is better to not fragmentate the code too much in functional components if the components of the application are not complex. Putting everything inside the main function in this case is the way to go. The code is more readable and easier to comprehend.
- I learned how to use short-circuit evaluation
- When used in functions, it is better to invoke state variables directly instead of passing them through arguments. E.g. the
img
argument in thehandleClick(img, side)
function is unnecessary - Not forcing the DRY (Don't Repeat Yourself) approach. I wanted to make one function for right and left buttons click event handling. The differences between the two cases appeared to me slight. I fooled myself believing I respected the DRY principle by doing this. In reality, I ended up producing a code not that optimized and unnecessarily more complex. Coding two functions for each arrows is a simpler, more readable, efficient and straightforward approach.
- nothing special
I would like to understand what is the best practice between using require
for loading images or .map
for preselecting images before loading them.
I would like to dive more into these:
- https://levelup.gitconnected.com/how-to-create-a-minimal-react-and-parcel-app-in-5-steps-2806fa09a371
- https://dev.to/egg_jose/how-to-create-a-react-app-without-create-react-app-command-1fgc
- deploy react app github: https://github.com/gitname/react-gh-pages
Use this section to outline areas that you want to continue focusing on in future projects. These could be concepts you're still not completely comfortable with or techniques you found useful that you want to refine and perfect.
- facebook/create-react-app Github : This helped me to know how to set up a react application with npm commmand
- Tic-tac-toe tutorial : This helped to build a react application
- How Can I Download a React Project from Github and Run in My PC : helped me to do the run locally section of the README and learn how to
- Allah who made this and everything possible
- ChatGPT (and extensivelly OpenAI and the people who produced the data GPT trained on) which helped me:
- to understand the map array part of the project poster solution.
- to embark into the process of analyzing the solution
- React.dev for their tic-tac-toe tutorial and their documentation
- Reed Barger, for providing these cool projects