DAY 7 : Mastering useState in React with a Beginner-Friendly Counter App | My Web Dev Journey (ReactJS)


Introduction
Welcome to Day 7 of my web development journey!
For the past few days, I’ve been diving into ReactJS after completing the fundamentals of HTML, CSS, and JavaScript.
I learnt concepts like state management, event handling,conditional rendering and build a project to grasp these concepts.
I'm sharing my learning process in public to stay consistent and hopefully help others who are on a similar path.
Here’s what I learnt in last 3 days:
Day 4:
→ Event Handling
→ State
→ useState() hookDay 5:
→ React Fragment
→ Condional RenderingDay 6:
→ Did Project to understand these concepts
→ Counter App
Let’s break down each of these topics below 👇
1. Event Handling:
Event Handling is the way we respond to user interactions, such as clicking button, typing in input field, etc..
React has its own Synthetic event system that wraps around the browser's native events to provide better performance and cross-browser compatibility.
In HTML/JS, We add event something like this:
<button onclick="doSomething()">Click Me</button>
But in React, the syntax is a bit different. Events are written in camelCase(onClick not onclick), and instead of a string, we pass a function reference.
How Event Handling Works in React?
Here’s a simple example I built while learning:
function App() {
const handleClick = () => {
console.log("Button clicked!");
};
return (
<div>
<h1>Hello React</h1>
<button onClick={handleClick}>Click Me</button>
</div>
);
}
export default App;
✅ onClick
is the event.
✅ handleClick
is the function that gets called when the button is clicked.
Things I Learned:
1. CamelCase Events
React uses camelCase for event names. So it’s onClick
, onChange
, onSubmit
, etc., not lowercase like in HTML.
2. Pass Function, Don’t Call It
I mistakenly wrote onClick={handleClick()}
and it ran automatically when the page loaded!
So I learned: always pass the function reference, not a function call.
3. Arrow Functions for Parameters
If I want to pass custom arguments to the function, I need to wrap it in an arrow function:
<button onClick={() => handleClick("React is awesome!")}>Click</button>
4. Synthetic Events
React wraps native events inside something called a SyntheticEvent. This ensures events work the same across all browsers. I didn’t dive too deep into it, but it’s good to know!
2. Understanding State:
In React, state refers to a way of tracking and managing data that can change over time in a component.
For example, if I want to show a counter that updates whenever I click a button, I need to store and update that number — and that’s where state comes in.
I learned that state helps React re-render the UI whenever something changes.
But wait, how do we create state in React?
That’s where the useState()
hook comes in!
3. useState() – My First React Hook:
useState() is a Hook that lets us add state to functional components.
It is local to the component.
Triggers re-render if state is updated.
Syntax:
const [stateVariable, setStateFunction] = useState(initialValue);
stateVariable
is the current value of the state.setStateFunction
is the function used to update the state.initialValue
is the initial value of the state(can be a number, string, object, arrays,etc..)
Example - Simple Counter:
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
function increase() {
setCount(count + 1);
}
return (
<div>
<h2>Count: {count}</h2>
<button onClick={increase}>Increase</button>
</div>
);
}
Every time I click the button, the increase
function is triggered, which updates the state using setCount
.
React then re-renders the component with the new count value
.
React ensures that new value
is shown on the screen.
Things I Learned:
State
allows components to react to user input or internal events.useState()
can only be used inside functional components — I tried it outside once and got an error!- State updates are asynchronous — so I learned not to expect
count
to update immediately in the next line. - I must never mutate the state directly, like doing
count++
. - Instead, always use the state updater function like:
setCount(count + 1)
.
4. React Fragment:
A React Fragment lets us group multiple elements without adding extra node to the DOM
.
Normally, JSX allows us to return only one parent element. So we often use a <div>
to wrap things — but too many <div>
can clutter the HTML and break the structure in some layouts. That’s where React Fragment comes in.
When we want to return multiple sibling elements from a Component
without wrapping them in extra <div>
, We use a react fragment.
How to use React Fragment:
1. Full Syntax:
const App = () => {
return (
<React.Fragment>
<h1>Hello</h1>
<p>This is inside a fragment</p>
</React.Fragment>
)
}
2. Short Syntax:
const App = () => {
return (
<>
<h1>Hello</h1>
<p>This is using short syntax</p>
</>
)
}
Things I Learned:
- React Fragment lets us return multiple elements without extra HTML nodes.
- Short syntax
<> </>
is super convenient but can’t accept props likekey
. - Use full syntax (
<React.Fragment>
) when we need to pass attributes.
5. Condional Rendering:
Conditional rendering in React means showing different UI elements based on certain conditions — like if-else
in JavaScript, but inside JSX!
For example, we can decide whether to show a message, a button, or even an entire component depending on user actions or app state.
Common Methods:
1. Using if Statements (Outside JSX)
This felt familiar from JavaScript. I used an if
block before the return statement.
Best for complex logic where multiple branches
are needed.
if (isLoggedIn) {
return <h1>Welcome back!</h1>;
}
return <h1>Please sign in.</h1>;
It’s clean but only works outside JSX, not directly inside the return()
.
2. Ternary Operator (Inline If-Else)
This is my favorite so far. I used it directly in JSX
.
<h1>{isLoggedIn ? "Welcome back!" : "Please sign in."}</h1>
- It's compact and neat.
- Easy to understand if the condition is simple.
- Great for rendering small changes in the
UI
.
3. Logical AND Operator (&&)
When I just wanted to show something only if a condition is true, I used &&
.
{isLoggedIn && <button>Logout</button>}
- If
isLoggedIn
istrue
, it renders the button. - If
false
, it renders nothing.
Super helpful for toggling UI elements.
One Mistake I Made:
I tried to put an if
condition directly inside JSX and React threw an error.
{/* This doesn’t work! */}
<h1>{if (isLoggedIn) { "Welcome!" }}</h1>
So, I learned that only expressions (not full statements) work inside JSX.
6. Counter App Project:
After learning React basics like useState
, event handling, and conditional rendering, I built a simple Counter App to apply these concepts in a real project.
This app allows the user to:
- Increment the counter value
- Decrement the counter value
- Reset the counter to zero
The main goal was to practice managing state, handling user events, and rendering elements conditionally based on the state.
Project Features:
- Increment the counter by
1
. - Decrement the counter by
1
. - Reset the counter to
0
. - Show error message when counter is at
0
(to avoid negative numbers). - Can change
step
value (by default it is1
).
Concepts I Applied:
1. useState Hook
I used useState()
to keep track of the:
- count
- input value
- error message
const [count, setCount] = useState(0); const [value, setValue] = useState(1); const [error, setError] = useState("");
2. Event Handling
I added onClick
handlers to Button Component to update the count:
<Button onClick={minusBtn} btnClass="minus" content="-" />
<Button onClick={plusBtn} btnClass="plus" content="+" />
<Button onClick={resetBtn} btnClass="reset-btn" content="Reset" />
Each button runs a function
that updates the state based on the user’s action.
3. Conditional Rendering
I show the error message
when count become negative using conditional rendering.
{error && <p className="err-message">{error}</p>}
This made the UI
feel interactive and dynamic!
Code:
1. App.jsx
import MainContainer from "./Components/MainContainer";
const App = () => {
return (
<div className="wrapper">
<MainContainer />
</div>
);
};
export default App;
2. MainContainer.jsx
import ContentContainer from "./ContentContainer";
function MainContainer() {
return (
<main>
<h1>Counter App</h1>
<ContentContainer />
</main>
);
}
export default MainContainer;
3. ContentContainer.jsx
import { useState } from "react";
import Button from "./Button";
import IncrementDecrement from "./IncrementDecrement";
import PlusMinusBtnContainer from "./PlusMinusBtnContainer";
const ContentContainer = () => {
const [count, setCount] = useState(0);
const [value, setValue] = useState(1);
const [error, setError] = useState("");
function resetBtn() {
setCount(0);
setValue(1);
setError("");
}
return (
<div className="container">
<p className="result">{count}</p>
<PlusMinusBtnContainer
count={count}
setCount={setCount}
value={value}
setError={setError}
/>
{error && <p className="err-message">{error}</p>}
<IncrementDecrement value={value} setValue={setValue} />
<Button onClick={resetBtn} btnClass="reset-btn" content="Reset" />
</div>
);
};
export default ContentContainer;
4. PlusMinusBtnContainer.jsx
import Button from "./Button";
const PlusMinusBtnContainer = ({ count, setCount, value, setError }) => {
function minusBtn() {
if (count - value >= 0) {
setCount(count - value);
setError("");
} else {
setError("Cannot go below zero");
}
}
function plusBtn() {
setCount(count + value);
setError("");
}
return (
<div className="plus-minus-btn-container">
<Button onClick={minusBtn} btnClass="minus" content="-" />
<Button onClick={plusBtn} btnClass="plus" content="+" />
</div>
);
};
export default PlusMinusBtnContainer;
5. IncrementDecrement.jsx
const IncrementDecrement = ({ value, setValue }) => {
function handleInput(e) {
const inputvalue = e.target.value;
if (inputvalue === "") {
setValue("");
} else {
setValue(Number(inputvalue));
}
}
return (
<p className="increment-decrement">
Increment/Decrement by:
<input
value={value}
onChange={handleInput}
className="number-input"
type="number"></input>
</p>
);
};
export default IncrementDecrement;
6. Button.jsx
const Button = ({ btnClass = "", content = "click", onClick }) => {
return (
<button onClick={onClick} className={`btn ${btnClass}`}>
{content}
</button>
);
};
export default Button;
Things I Learned:
- How
useState
keeps track of changes and re-renders the UI. - The importance of passing function references, not calling them directly.
- How to conditionally show elements using
&&
or ternary operators. - That even a basic project can reinforce multiple core concepts!
7. What's next:
I’m continuing this journey and will be:
- Posting blogs every 3 days
- Also learning DSA using Java — check out my DSA Journey Blog
- You can follow my journey on Follow me on X (Twitter) where I post regular updates.
8. Conclusion:
This part of my React journey helped me understand some of the most important building blocks of any React app:
- How events are handled differently in React using camelCase syntax.
- The concept of
state
and howuseState()
allows us to make our components dynamic and interactive. - The role of React Fragments in keeping our DOM clean without unnecessary wrapper elements.
- How to show or hide elements using conditional rendering techniques like
&&
and ternary operators. - And finally, how a simple project like a Counter App can bring all these concepts together in a practical and fun way!
Every small project or topic I explore builds a stronger foundation, and I’m excited to keep learning and building more with React!
If you're on a similar journey, feel free to reach out or follow along — we’re all in this together.
Subscribe to my newsletter
Read articles from Ritik Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ritik Kumar
Ritik Kumar
👨💻 Aspiring Software Developer | MERN Stack Developer.🚀 Documenting my journey in Full-Stack Development & DSA with Java.📘 Focused on writing clean code, building real-world projects, and continuous learning.