0% found this document useful (0 votes)
14 views30 pages

Tutorial 5 - Introduction To ReactJS (Part II)

This tutorial introduces key concepts in ReactJS, focusing on managing component state using the useState hook, which allows for dynamic UI updates. It covers event handling, including how to create interactive components through event handlers, and emphasizes the importance of immutability and unique keys when rendering lists. Additionally, it discusses conditional rendering techniques and how to enhance a to-do list application with features like adding and toggling to-do items.

Uploaded by

chulyy2202
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views30 pages

Tutorial 5 - Introduction To ReactJS (Part II)

This tutorial introduces key concepts in ReactJS, focusing on managing component state using the useState hook, which allows for dynamic UI updates. It covers event handling, including how to create interactive components through event handlers, and emphasizes the importance of immutability and unique keys when rendering lists. Additionally, it discusses conditional rendering techniques and how to enhance a to-do list application with features like adding and toggling to-do items.

Uploaded by

chulyy2202
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 30

Tutorial 5: Introduction to ReactJS (Part II)

I. State: Making UIs Interactive


1. The useState Hook: Managing Component State

In React, state allows components to manage and respond to changes in data


internal to the component. Unlike props, which are passed to a component by its
parent, state is managed within the component itself. Whenever a component's
state changes, React automatically re-renders the component, ensuring that the UI
(User Interface) reflects the latest data. This dynamic updating is the foundation of
interactive React applications. The useState hook is the primary way to manage
state within functional components.

useState Syntax and Behavior:

Let's break down what useState(0) does:

Initializes State: It initializes a state variable. In this case, a state variable named
count is created and initialized with a value of 0. The argument you pass to
useState (in this example it's 0) determines the initial value of your state variable.
This initial value can be any valid Javascript value, including a number, string,
boolean, array, object, or null.
Returns a Pair: The useState hook returns an array containing two elements:

 The current state value. (e.g., count in the example above)


 A function to update the state. (e.g., setCount) This function is the only way
you should update a state variable in React. It takes one argument - the new
state value.

Array Destructuring: const [count, setCount] = useState(0); uses array


destructuring to assign the two returned values directly to the variables count and
setCount. It is just a more concise and readable equivalent to:

Triggering Re-renders: Calling the state updater function (e.g.,


setCount(newValue)) performs two crucial actions:

 It updates the state variable to the new value you pass to the function.
 Critically, it also notifies React that the state has changed, causing React to
re-render the component. Re-rendering ensures that what the user sees in the
user interface stays synchronized with the latest application state.

Simple Counter Example:


Initially, the UI shows "Count: 0" and a button. Each button click calls the
increment function. increment() uses setCount to update the state. setCount triggers
a re-render of the Counter component, updating the displayed count dynamically.

2. Immutability: A Key Concept in React

Immutability is essential when working with React state, especially with objects
and arrays. Directly modifying (mutating) existing state can lead to unexpected
issues because it interferes with React's efficient change detection mechanism.
Always create a new object or array representing the updated state, leaving the
original state unchanged.

Incorrect (Direct Mutation - Avoid This):

Correct (Immutable Update Using the Spread Operator):


Why Immutability is Crucial in React: React's change detection mechanism (which
determines when to re-render a component) works by comparing object references.
When you create a new object or array during a state update, React detects the
change in reference and knows to re-render the component to reflect the new state.
If you modify the existing state directly (mutation), the reference remains the
same, even though the underlying object's properties or array elements change.
Consequently, React might not detect the change, and the UI could become out of
sync with the application data. Always create new values for state updates – this is
a fundamental principle in React, and critical for reliable rendering.

Back to project: Initializing todos State in App.js

Now, let's apply the concept of state to your to-do list application. Open the App.js
file and initialize the state to store an array of to-do objects.
Each to-do object in the state has the following structure:

id: 1, //Unique ID

text: "Buy groceries", //Text of to-do item

completed:false //Completion status

II. Event Handling


1. Event Handlers in React

Event handlers are functions that execute in response to user interactions with UI
elements, such as mouse clicks, keyboard input, form submissions, and other
actions. React's event system is similar in concept to standard JavaScript event
handling but with some key differences (like the use of synthetic events, which
normalize event behavior across different browsers, a more advanced concept that
we will touch on later). Event handlers are essential for making your React
components interactive, enabling your application to respond dynamically to user
input and actions. Event handlers in JSX are attached to elements as attributes. The
value of an event handler attribute should always be a function.

Here are some of the most frequently used event handlers:

 onClick: Handles click events. This is probably the most common event
handler, and it executes a function when a user clicks on an element.
 onChange: Handles change events. Frequently used with form elements such
as <input>, <select>, and <textarea>. This event triggers whenever the user
modifies the value of an input element (by typing, selecting a new option,
etc.). It's fundamental to implementing controlled components in React,
which we'll examine in the project section. Controlled components are a key
concept in React forms.
 onSubmit: Handles form submission events. Triggered when a user submits
a form, usually by clicking a submit button or pressing the Enter key within
a form field.
 onMouseOver: Handles mouse hover events - triggered when the mouse
pointer moves over an element. Often used for things like highlighting UI
elements or showing tooltips.
 onMouseOut: The counterpart to onMouseOver, triggered when the mouse
pointer moves out of an element (after having been over it). Used in
conjunction with onMouseOver for hover effects.
 onFocus: Handles focus events, which occur when an element gains
keyboard focus (e.g. after clicking on it or tabbing to it with the keyboard).
Often used with form fields to provide visual feedback to the user, or show
some kind of help text.
 onBlur: Handles blur events, triggered when an element loses keyboard
focus. Often used with forms for validation purposes - for instance, you
might check if an email field contains a valid address only after the user has
finished typing in the email and has moved on to another field by tabbing or
clicking away from the email field.

Passing Event Handlers as Props: Event handlers are most commonly defined
within a parent component and then passed down to child components as props
(properties). This pattern lets the parent component manage how its children
respond to events, facilitating better code organization, component reusability, and
preventing the logic for event handling from becoming too spread out amongst
several different components.

Basic Examples - Button Click Counter:

The component renders a single button that says "Click me!". Clicking the button
will update both the text on the button to "Clicked 1 times" and also update the
internal clickCount state. Each click increments the counter. This basic example
shows the pattern of defining the click handler function inside the component,
attaching it to an element, and how that handler can modify the component's
internal state. Note that the state update call setClickCount uses a function – this
ensures that we're using the latest value for the state, which is important because
state updates can be asynchronous.

Controlled Input: This example illustrates a controlled component - a crucial


concept in React where the component's state governs the value of an input
element.

The component renders a text input and a paragraph displaying "Current input: ".
As the user types in the input field, the text they are typing immediately and
dynamically appears below the textbox in the p tag. This is because the onChange
handler is triggered on every keystroke or change in the input's value.
handleChange updates the inputValue state, causing the component to re-render
with the latest value entered by the user. This dynamic update and synchronization
of the input's value with the component's state is what's meant by a "controlled
component." This approach might seem slightly counterintuitive at first to anyone
familiar with standard HTML form handling, but it's central to managing form
input in React efficiently.

Back to Project - Adding "Add Todo" Functionality

Let's implement the functionality to add new to-do items to your to-do list app.

Add Input Field and Button (JSX): Within App.js, inside the return statement of
the App component, add an input field and a button for adding new items:

Input Value State: Add a state variable called newTodoText to manage the text
entered in the new to-do input field.

Implement handleInputChange (onChange): Create the onChange handler for the


input field to update state as the user types.

Implement addTodo (onClick): Create the onClick handler to add a new to-do item
to the list when the button is clicked.
Connect to JSX (Controlled Component, Event Handlers):

Complete and Detailed Updated App.js Code:


You should now have a working add to-do functionality. As you type in the input
field, the text appears in the input field. Clicking "Add Todo" adds the entered text
to the to-do list (state is updated), and the input clears, ready for the next entry.
This demonstrates basic form handling and dynamic updates in React, a
fundamental aspect of building interactive UIs.

III. Mapping over Lists and Keys


1. map() in JSX: Transforming Arrays to JSX

The .map() method is a higher-order function in JavaScript—a function that


operates on other functions. Its primary role is to transform arrays. It takes a
function as an argument and applies that function to each element of an array,
creating and returning a new array containing the results of these function calls.
Crucially, the original array is not modified. This makes it extremely useful for
dynamically generating lists of JSX elements or React components from an array
of data, which is a very common pattern in React applications.

map() Examples:
map() in JSX: Within JSX, .map() dynamically generates lists of components or
elements from arrays, bringing data to life in the UI.
Output for element: An unordered list (<ul>) containing list items (<li>) for each
number (1, 2, 3). .map() transformed the numbers array into an array of JSX list
item elements. Output for the second example using the NumberList component
would be the same.

2. Why Keys are Important:

When rendering lists, React uses keys to optimize DOM updates. Keys help React
identify which items have changed, been added, or removed. They're essential for
performance and predictable behavior when working with dynamic lists. React
uses a "diffing" algorithm to update the DOM, and keys enable quick
determination of changes rather than a more time-consuming, less efficient full re-
render, especially for lists with hundreds or thousands of items.

3. Keys: Unique Identifiers for List Items

Uniqueness is Key: Keys must be unique among sibling elements within the same
array.map() call. They don't need to be globally unique across the entire
application.
Why Use Keys? Keys enable React's efficient DOM updating mechanism. Without
keys, React might re-render the entire list whenever a change occurs. Keys
dramatically improve performance, especially for larger lists, and are crucial for
state management when lists involve form elements. If keys are not unique within
the same list, strange behavior can occur where React might set the state from one
to-do item onto another item, or render the incorrect items, which makes
debugging difficult.

Correct and Incorrect Key Usage:

Correct (Unique, Stable Identifiers from your data): The best practice is to use a
unique ID from your data as the key.

This is highly encouraged for dynamic data where ID is often the primary key in a
database.

Incorrect (Index as Key – Avoid except for completely static lists): Using
array index as the key is discouraged, except when the following conditions
hold true:
The array or list is completely static - nothing is added to or removed from it, and
it is rendered only once.

The items in the array have no identity – they are completely interchangeable.

And even then, if you can use a more meaningful ID, you should still prefer that.

If the data is static and the items have no unique identifiers, you could consider
adding an explicit ID or using other methods to guarantee uniqueness if your data
is dynamic.

Back to Project: Rendering TodoItem Components Dynamically

In App.js, let's dynamically create TodoItem components from the todos array,
passing relevant data as props.
The application renders the initial to-do list from state. You can type a new todo
into the textbox and when you click the "Add Todo" button the new to-do is added
to the list, and the input text is cleared. Clicking on to-do list items toggles their
completed status. The project now demonstrates that for every to-do item object in
our todos array, React will create a TodoItem component. todo.text and
todo.completed from each to-do object are passed to the TodoItem as props,
ensuring dynamic content. Critically, todo.id is used as the key for each rendered
TodoItem, ensuring that React can properly track and update the DOM as items are
added or changed. Don't underestimate the importance of unique keys – they might
seem like a small detail at this stage, but are absolutely fundamental for how React
efficiently updates the UI and prevents strange bugs if you start adding/removing
or modifying your to-do items.

IV. Conditional Rendering and Basic Styling


1. Conditional Rendering in React

Conditional rendering is fundamental to building dynamic and interactive user


interfaces in React. It empowers you to display different UI elements or
components based on conditions such as the application's state, user interactions, or
the availability of data. This adaptability makes UIs more responsive and user-
friendly.

Several techniques facilitate conditional rendering in React:

1.1. Ternary Operator:

The most concise way to express simple conditional rendering directly within JSX.
It's a shorthand for the if/else statement, making your JSX cleaner and more
readable when dealing with basic conditionals.
If isLoggedIn is true, the <h1> is rendered; otherwise, the <p> is rendered. It is
crucial that only one JSX element or a React Fragment is returned by the ternary
expression in this case. If you have multiple JSX elements in one or both branches
of the ternary operator, wrap them in a React Fragment <> </>.

1.2. Logical AND Operator (&&):

This operator provides a more concise way to render something only if a condition
is true, perfect when there is no alternative content to display if the condition is
false.

This relies on JavaScript's short-circuit evaluation—if showMessage is false, the


right-hand side of && (the JSX) isn't evaluated, and nothing is rendered.

1.3. Conditional Chaining (Nested Ternaries):

For handling more than two conditions, you can chain ternary operators, though
excessive chaining quickly becomes unreadable. Use it sparingly. For more
complex logic, alternative methods are better. Avoid these for larger numbers of
conditions if possible, for readability reasons and for easier debugging.

1.4. if/else Statements (Use with Caution in JSX return Statements):

Standard JavaScript if/else statements work perfectly well inside the body of
functional components, before the return statement. However, placing large or
complex if/else blocks directly within the JSX that is returned from a component
can make your JSX less readable, especially as your components grow in size and
complexity.
For more complex conditional logic, consider these better practices:

 Early Return: For single conditionals, return early from your component
function if the condition is met, simplifying the remaining logic.
 Component Extraction: Refactor conditional blocks into separate, smaller
components. Pass data via props to each component. This creates modular,
reusable code and makes the main component much easier to read and
understand. This is highly recommended for more complex conditional
logic.

Back to Project: Styling and Toggling Completed To-Dos

Enhance the to-do list application with styling and a toggle feature.
Conditional Styling in TodoItem.js: Apply text-decoration: line-through to
completed to-dos.

toggleTodo Function in App.js: Add a function called toggleTodo to your App.js


component. This function will be called whenever the user clicks a to-do list item.
It takes the id of the todo item as a parameter and uses that ID to locate the correct
to-do item and toggle its state immutably.
Pass toggleTodo to TodoItem: Pass the function down to the TodoItem in your
map function in App.js. Pass a new function to each TodoItem component rather
than calling the function directly as that would execute the toggleTodo function
immediately, which we don't want. Instead we wrap the function call
toggleTodo(todo.id) in an anonymous arrow function that will only execute when
the list item is clicked.

Complete App.js:
Your to-do list displays the initial items. Clicking an item toggles its completed
status, dynamically updating its style (adding or removing the line-through effect).
The application starts with the hardcoded to-do items and you can add new to-do
list items using the input field and Add Todo button. Input values will dynamically
appear in the input box.
V. Wrap-up
1. Recap of Key Concepts

We've explored the fundamental building blocks of React development:

 JSX (JavaScript XML): This syntax extension lets you write HTML-like
code within your JavaScript, making UI definitions more concise, readable,
and easier to manage, especially for larger, more complex UIs. JSX is not
HTML; it's a way to represent HTML elements within JavaScript, which is
then transpiled into regular JavaScript that a browser can understand and
execute.
 Components: These are reusable, self-contained UI elements that
encapsulate both structure (JSX) and logic (JavaScript). Components are the
heart of React's modular architecture. They enable building complex UIs by
composing smaller, simpler units.
 Props (Properties): Props provide a mechanism to pass data from parent
components down to their children. They function like arguments passed to
a function, giving child components data to customize their appearance and
behaviour. Props enhance reusability and modularity in React applications.
 State: State represents data managed within a component. Unlike props,
which are passed in from the outside, a component's state can change over
time, typically in response to events or user interactions. The useState hook
is the standard mechanism for working with state in functional components.
useState returns the current state value and a function to update that state.
Whenever the component's state changes, React re-renders the component to
reflect the changes in the user interface. Immutability is a crucial principle
when updating state, especially with objects and arrays. You should always
create new state values when making changes, rather than directly modifying
existing state, to ensure reliable rendering and efficient change detection by
React.
 Event Handling: Event handling is how your components interact with user
actions (clicks, input changes, form submissions, etc.). You define event
handler functions and attach them to JSX elements via attributes like
onClick, onChange, etc. Event handlers are often defined in parent
components and then passed as props to their children.
 Conditional Rendering: This involves displaying different UI elements based
on conditions (application state, prop values, user input). Techniques include
the ternary operator (condition ? valueIfTrue : valueIfFalse), logical AND
operator (condition && elementToRender), and (less commonly, and
cautiously) if/else blocks within the JSX of a component's return statement.
For complex conditionals, it is generally preferable to keep the logic outside
of the main render method of a component and instead encapsulate that
conditional logic into a separate helper function, or better still, extract the
conditional parts of the UI into completely separate function components,
passing data to those components via props as needed.
 Lists and Keys: To render dynamic lists of components or JSX elements, use
the .map() method on an array. The map method calls a callback function
you supply once for every item in the array, and you return the new
transformed value from the callback. These new/transformed values are
assembled into a new array by the map function, and this array is what you
use within your JSX to render the list. Critically, each dynamically rendered
item in a React list must have a unique key prop. These keys enhance
React's rendering efficiency, particularly for large lists, by enabling targeted
updates rather than full list re-renders, and for persisting state between re-
renders if list items contain form elements or similar.
 Basic Styling: You can style your React components using standard CSS.
Inline styles (within JSX elements' style attribute) are not recommended for
larger projects because it makes it harder to maintain consistency across
components. Prefer external stylesheets linked using <link> tags, applying
styles with the className attribute in JSX.
2. Next Steps and Further Learning

Having learned the basics, consider these paths for continued learning:

 Advanced Forms: Learn about handling complex form elements, validation


techniques, handling form submission to a back-end API, and handling
asynchronous operations.
 Fetching Data from APIs: Explore techniques for fetching data from external
APIs using methods like fetch or axios. Combine this with the useEffect
hook (covered in more detail later), to manage data fetching and updates
efficiently in functional components.
 Advanced Styling Techniques (CSS Modules, Styled Components, CSS-in-
JS libraries, and Utility-First CSS): There are many different methods
available now for styling React applications. CSS modules let you locally
scope styles to specific components. Styled Components or Emotion (CSS-
in-JS libraries) allow you to write CSS-like syntax within JavaScript
components, generating unique class names to avoid styling conflicts.
Utility-first frameworks like Tailwind CSS have become very popular and
offer an alternative, more atomic CSS approach where you use a predefined
set of utility classes to style your user interface.
 Client-Side Routing (React Router): Implement client-side routing using
React Router. This allows you to create single-page applications (SPAs)
with multiple views or screens, where navigation changes the content
without reloading the entire page, significantly enhancing user experience.
 Advanced State Management (Context API, Redux, Jotai, Zustand): Explore
different methods for managing the complexity of state as applications
become more sophisticated. React's built-in Context API provides a good
initial solution for sharing state globally without prop drilling. External state
management libraries like Redux offer a robust but more complex solution
for larger projects. Other state management libraries like Zustand and Jotai
offer other alternatives worth looking into, and each library offers various
trade-offs and should be explored on a case-by-case basis when starting a
larger React project.
 Testing: Learning about writing unit and integration tests for your React
components using tools like Jest and React Testing Library is another
important subject to learn. This ensures quality and maintainability of your
applications as they grow.

You might also like