find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +
Firebase endpoint: https://react-complete-course-85dc9-default-rtdb.firebaseio.com/
A javascript library for building User Interfaces based on reusable components.
traditionally any button on a website triggered a request from a server to load a new html page.
Javascript allows us to manipulate the DOM without loading a new page and react is a JS library
in react building components using a series of smaller components is called composition
import React from "react";
import "./Card.css";
function Card(props) {
return <div className="card">{props.children}</div>;
}
export default Card;
props.children is important for composition using a custom wrapper component often used to apply styles that are shared among components
in package.json we use react and reactDOM
import React from "react";
// when you use jsx it is this(React.createElement ... requires importing React from react) method that gets called behind the scenes.
/* the second argument is an object tha configures atributes of element... in here none so empty object*/
// the third and subsequent arguments are the children of each sucessivly nested element or component.
return React.createElement(
"div",
{},
React.createElement("h2", {}, `Let's get started!`),
React.createElement(Expenses, { items: expenses })
);
document.getElementById("root").addEventListener("click", () => {
console.log("clicked");
});
In react we add a special prop to the element we want to listen to and set it to a function that will be executed when the event occurs.
React exposes events as props that start with prefix on… so onClick, onChange, onSubmit, etc.
i.e.
<button onClick={clickHandler}>Change Title</button>
//here we are just pointing to the function and not calling it
all the on-event handler props want a function passed as a value which will be executed when the event occurs.
const clickHandler = () => {
setTitle("Updated!");
console.log(title);
};
useState is a react hook that allows you to manage state in functional components.
// let title = props.title;
const [title, setTitle] = useState(props.title);
import React, { useState } from "react";
here {UseState} is a
named importfrom react
const [title, setTitle] = useState(props.title);
const clickHandler = () => {
title = "Updated!";
console.log(title);
};
- becomes
const clickHandler = () => {
setTitle("Updated!");
console.log(title);
};
import React, { useState } from "react";
import ExpenseDate from "./ExpenseDate";
import Card from "../UI/Card";
import "./ExpenseItem.css";
const ExpenseItem = (props) => {
// let title = props.title;
const [title, setTitle] = useState(props.title);
console.log("ExpenseItem evaluated by React");
const clickHandler = () => {
setTitle("Updated!");
console.log(title);
};
return (
<Card className="expense-item">
<ExpenseDate date={props.date} />
<div className="expense-item__description">
<h2>{title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
<button onClick={clickHandler}>Change Title</button>
</Card>
);
};
export default ExpenseItem;
const [title, setTitle] = useState(props.title);
document.getElementById("root").addEventListener("click", (event) => {
console.log("clicked");
});
const titleChangeHandler = (event) => {
setUserInput({ ...userInput, enteredTitle: event.target.value });
};
const amountChangeHandler = (event) => {
setUserInput({ ...userInput, enteredAmount: event.target.value });
};
const dateChangeHandler = (event) => {
setUserInput({ ...userInput, enteredDate: event.target.value });
};
takes all of the properties of the userInput object and adds them to a new object. It then overwrites the enteredTitle property with the new value. This is called merging objects. It is a common pattern in react to merge objects when you want to update a state property that is an object.
import React, { useState } from "react";
import "./ExpenseForm.css";
const ExpenseForm = () => {
// const [enteredTitle, setEnteredTitle] = useState("");
// const [enteredAmount, setEnteredAmount] = useState("");
// const [enteredDate, setEnteredDate] = useState("");
const [userInput, setUserInput] = useState({
enteredTitle: "",
enteredAmount: "",
enteredDate: "",
});
const titleChangeHandler = (event) => {
console.log("title change event: value:", event.target.value);
// setEnteredTitle(event.target.value);
setUserInput((previousState) => {
return { ...userInput, enteredTitle: event.target.value };
});
};
const amountChangeHandler = (event) => {
console.log("amount change event: value:", event.target.value); //event.target.value is a string even if the input type is number
// setEnteredAmount(event.target.value);
setUserInput((previousState) => {
return { ...userInput, enteredAmount: event.target.value };
});
};
const dateChangeHandler = (event) => {
console.log("date change event: value:", event.target.value);
// setEnteredDate(event.target.value);
setUserInput((previousState) => {
return { ...userInput, enteredDate: event.target.value };
});
};
return (
<form>
<div className="new-expense__controls">
<div className="new-expense__control">
<label>Title</label>
<input type="text" onChange={titleChangeHandler} />
</div>
<div className="new-expense__control">
<label>Amount</label>
<input
type="number"
min="0.01"
step="0.01"
onChange={amountChangeHandler}
/>
</div>
<div className="new-expense__control">
<label>Date</label>
<input
type="date"
min="2019-01-01"
max="2023-12-31"
onChange={dateChangeHandler}
/>
</div>
</div>
<div className="new-expense__actions">
<button type="submit">Add Expense</button>
</div>
</form>
);
};
export default ExpenseForm;
const submitHandler = ( event ) => {
event.preventDefault();
const expenseData = {
title: enteredTitle,
amount: enteredAmount,
date: new Date( enteredDate ),
};
return (
<form onSubmit={ submitHandler}>
In React, data flows one way: from owner to child. We think that this makes your app’s code easier to understand. You can think of it as “one-way data binding.”
However, there are lots of applications that require you to read some data and flow it back into your program. For example, when developing forms, you’ll often want to update some React state when you receive user input. Or perhaps you want to perform layout in JavaScript and react to changes in some DOM element size.
In React, you would implement this by listening to a “change” event, read from your data source (usually the DOM) and call setState() on one of your components. “Closing the data flow loop” explicitly leads to more understandable and easier-to-maintain programs.
Two-way binding — implicitly enforcing that some value in the DOM is always consistent with some React state — is concise and supports a wide variety of applications. We’ve provided LinkedStateMixin: syntactic sugar for setting up the common data flow loop pattern described above, or “linking” some data source to React state.
<input type="text" value="" onChange={titleChangeHandler} />
// onChange is a prop that wants a function as a value
const submitHandler = (event) => {
event.preventDefault();
const expenseData = {
title: enteredTitle,
amount: enteredAmount,
date: new Date(enteredDate),
};
console.log(expenseData);
setEnteredTitle("");
setEnteredAmount("");
setEnteredDate("");
};
We can clear the data after hittin submit using the following code:
setEnteredTitle("");
setEnteredAmount("");
setEnteredDate("");
```
we pass data from parent to child via props and from child to parent via function props.
We can pass data from child to parent via function props.We can create our own event props that expect functions as values which allows us to pass a function from a parent component to a child component and then call that function inside of the child component. When we call said function we can pass data to that function as a parameter and that data will then be passed back to the parent component.
props can only be passed from parent component to child and we can’t skip intermediate components.
Let’s say we want to pass expense data which we gather in the expense form component to the new expense component. We can do this by passing a function from the new expense component to the expense form component and then call that function inside of the expense form component and pass the data as a parameter to that function.
import React from "react";
import "./NewExpense.css";
import ExpenseForm from "./ExpenseForm";
const NewExpense = (props) => {
//the value for onSaveExpenseData should be a function that is triggered when the user clicks the submit button.
const onSaveExpenseDataHandler = (enteredExpenseData) => {
const expenseData = {
...enteredExpenseData,
id: Math.random().toString(),
};
console.log("expense data enriched with id property", expenseData);
props.onAddExpense(expenseData);
};
//the value for onSaveExpenseData should be a function that is triggered when the user clicks the submit button... we can pass data as an argument to onSaveExpenseDataHandler to pass that data from the child component to the parent component.
//onSaveExpenseDataHandler is a function that is passed as a value to onSaveExpenseData ... it does not get executed here, hence the absence of (). It will be exicuted in the expense form component when the user clicks the submit button.
return (
<div className="new-expense">
<ExpenseForm onSaveExpenseData={onSaveExpenseDataHandler} />
</div>
);
};
export default NewExpense;
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity.
A “key” is a special string attribute you need to include when creating lists of elements in React. Keys are used in React to identify which items in the list are changed, updated, or deleted. In other words, we can say that keys are used to give an identity to the elements in the lists. The next thing that comes to mind is that what should be good to be chosen as key for the items in lists. It is recommended to use a string as a key that uniquely identifies the items in the list. Below example shows a list with string keys:
const numbers = [1, 2, 3, 4, 5];
const updatedNums = numbers.map((number) => {
return <li key={index}>{number} </li>;
});
You can also assign the array indexes as keys to the list items. The below example assigns array indexes as key to the elements.
const numbers = [1, 2, 3, 4, 5];
const updatedNums = numbers.map((number, index) => (
<li key={index}>{number}</li>
));
Assigning indexes as keys are highly discouraged because if the elements of the arrays get reordered in the future then it will get confusing for the developer as the keys for the elements will also change.
Consider a situation where you have created a separate component for list items and you are extracting list items from that component. In that case, you will have to assign keys to the component you are returning from the iterator and not to the list items. That is you should assign keys to
import React from "react";
import ReactDOM from "react-dom";
// Component to be extracted
function MenuItems(props) {
const item = props.item;
return <li key={item.toString()}>{item}</li>;
}
// Component that will return an
// unordered list
function Navmenu(props) {
const list = props.menuitems;
const updatedList = list.map((listItems) => {
return <MenuItems item={listItems} />;
});
return <ul>{updatedList}</ul>;
}
const menuItems = [1, 2, 3, 4, 5];
ReactDOM.render(
<Navmenu menuitems={menuItems} />,
document.getElementById("root")
);
**Output: **

You can see in the above output that the list is rendered successfully but a warning is thrown to the console that the elements inside the iterator are not assigned keys. This is because we had not assigned key to the elements we are returning to the map() iterator.
Below example shows correct usage of keys:
import React from "react";
import ReactDOM from "react-dom";
// Component to be extracted
function MenuItems(props) {
const item = props.item;
return <li>{item}</li>;
}
// Component that will return an
// unordered list
function Navmenu(props) {
const list = props.menuitems;
const updatedList = list.map((listItems) => {
return <MenuItems key={listItems.toString()} item={listItems} />;
});
return <ul>{updatedList}</ul>;
}
const menuItems = [1, 2, 3, 4, 5];
ReactDOM.render(
<Navmenu menuitems={menuItems} />,
document.getElementById("root")
);
The style prop on a React element accepts a JavaScript object with camelCased properties rather than a CSS string. This is consistent with the DOM style JavaScript property.
<div className="chart-bar__fill" style={{}}></div>
We use the css property names as key names.
<div className="chart-bar__fill" style={{ height: "15%" }}></div>
for..of and for..in statements iterate over lists; the values iterated on are different though, for..in returns a list of keys on the object being iterated, whereas for..of returns a list of values of the numeric properties of the object being iterated.let list = [4, 5, 6];
for (let i in list) {
console.log(i); // "0", "1", "2",
}
for (let i of list) {
console.log(i); // "4", "5", "6"
}
Another distinction is that for..in operates on any object; it serves as a way to inspect properties on this object. for..of on the other hand, is mainly interested in values of iterable objects. Built-in objects like Map and Set implement Symbol.iterator property allowing access to stored values.
let pets = new Set(["Cat", "Dog", "Hamster"]);
pets["species"] = "mammals";
for (let pet in pets) {
console.log(pet); // "species"
}
for (let pet of pets) {
console.log(pet); // "Cat", "Dog", "Hamster"
}
import React, { useState } from "react";
import Button from "../../UI/Button/Button";
import "./CourseInput.css";
import styled from "styled-components";
//-------------------------------Form Control Component--------------------------------------
const FormControl = styled.div`
margin: 0.5rem 0;
& label {
font-weight: bold;
display: block;
margin-bottom: 0.5rem;
}
& input {
display: block;
width: 100%;
border: 1px solid ${(props) => (props.invalid ? "red" : "#ccc")}};
background: ${(props) => (props.invalid ? "#fad0ec" : "transparent")};
font: inherit;
line-height: 1.5rem;
padding: 0 0.25rem;
}
& input:focus {
outline: none;
background: #fad0ec;
border-color: #8b005d;
}
/* the following applies to input elements that have class name form-control and invalid*/
&.invalid input {
border-color: red;
background: #fad0ec;
}
&.invalid label {
color: red;
}
`;
div soup which is when you end up with tons of unnecessary divs in your code that add no semantic meaning or structure to the page but are just there to wrap other elements because React requires a single root element.
i.e.
<div>
<div>
<div>
<div>
<div>
<h2>'Div Soup'</h2>
</div>
</div>
</div>
</div>
</div>
return (
<React.Fragment>
<h2>Fragment Demo</h2>
<p>This is a fragment demo</p>
</React.Fragment>
);
Or you can use the shorthand syntax:
return (
<>
<h2>Fragment Demo</h2>
<p>This is a fragment demo</p>
</>
);
Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
return (
<React.Fragment>
<MyModal />
<InputForm />
</React.Fragment>
);
We can use a portal to render the modal outside of the component it was created in somewhere else on the actual DOM
Refs - Refs provide a way to access DOM nodes or React elements created in the render method.
AddUser.js
import React, { useState, useRef } from "react";
import Wrapper from "../Helpers/Wrapper";
import Button from "../UI/Button";
import Card from "../UI/Card";
import ErrorModal from "../UI/ErrorModal";
import classes from "./AddUser.module.css";
const AddUser = (props) => {
const nameInputRef = useRef(); //useRef() returns an object with a property called current which is a pointer to the element we want to focus on
const ageInputRef = useRef();
const [enteredUsername, setEnteredUsername] = useState("");
const [enteredAge, setEnteredAge] = useState("");
const [error, setError] = useState();
const addUserHandler = (event) => {
event.preventDefault();
console.log(nameInputRef);
if (enteredUsername.trim().length === 0 || enteredAge.trim().length === 0) {
setError({
title: "Invalid input",
message: "Please enter a valid name and age (non-empty values).",
});
return;
}
if (+enteredAge < 1) {
setError({
title: "Invalid age",
message: "Please enter a valid age (> 0).",
});
return;
}
props.onAddUser(enteredUsername, enteredAge);
setEnteredUsername("");
setEnteredAge("");
};
const usernameChangeHandler = (event) => {
setEnteredUsername(event.target.value);
};
const ageChangeHandler = (event) => {
setEnteredAge(event.target.value);
};
const errorHandler = () => {
setError(null);
};
return (
<Wrapper>
{error && (
<ErrorModal
title={error.title}
message={error.message}
onConfirm={errorHandler}
/>
)}
<Card className={classes.input}>
<form onSubmit={addUserHandler}>
<label htmlFor="username">Username</label>
<input
id="username"
type="text"
value={enteredUsername}
onChange={usernameChangeHandler}
ref={nameInputRef}
/>
<label htmlFor="age">Age (Years)</label>
<input
id="age"
type="number"
value={enteredAge}
onChange={ageChangeHandler}
ref={ageInputRef}
/>
<Button type="submit">Add User</Button>
</form>
</Card>
</Wrapper>
);
};
export default AddUser;
The first time React reaches the code on the return statment of the AddUser component it will create a new object and store it in the nameInputRef variable. This object will have a property called current which will be a pointer to the input element with the id of username. This object will be stored in the nameInputRef variable and will be available for the entire lifetime of the AddUser component.
Controlled Components -Manages its own internal state and reacts to user input. The state is updated by the event handlers. The state is then passed to the input element as a value prop. The input element then displays the value of the state.
Uncontrolled Components - Use refs to interact with access DOM nodes or React elements created in the render method. They are uncontrolled because their internal state is not controlled by react. They are controlled by the DOM… we just use react refs to fetch the data from the DOM.
Effects (also known as Side Effects) - Effects are used to perform side effects in functional components. Side effects are things that happen outside of the component. For example, making an HTTP request, accessing the browser local storage, etc. Effects are triggered by changes to the component’s state or props. They are also triggered by changes to the component’s dependencies. Dependencies are values that the effect depends on. For example, if the effect depends on the value of a prop, then the effect will be triggered when the prop changes.
Reducers - Reducers are functions that take the current state and an action as arguments and return a new state result. Reducers are pure functions. They should not perform any side effects. They should not mutate the state. They should not access the DOM. They should not access global variables. They should not access the arguments object. They should not access the this keyword. They should not access the event object. They should not access the event target. They should not access the event target value. They should not access the event target checked. They should not access the event target files. They should not access the event target name. They should not access the event target type. They should not
Context - Context provides a way to pass data through the component tree without having to pass props down manually at every level. Context is primarily used when some data needs to be accessible by many components at different nesting levels. Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.
UseEffect() - useEffect() is a hook that allows us to execute code when a component is rendered for the first time or when a component is re-rendered. It is a combination of componentDidMount() and componentDidUpdate().
useEffect(() => {}, []); //useEffect() is a hook that allows us to execute some code when a component is rendered for the first time or when it is re-rendered
The following would only run once because it has no dependencies to signal for it to run again on a change in dependency
useEffect(() => {
const loginState = localStorage.setItem("isLoggedIn", "1");
if (loginState === "1") {
setIsLoggedIn(true);
}
}, []);
Removing the dependency array will cause the useEffect() to run on every render cycle.
the following code will run whenever there is a change to the enteredEmail or enteredPassword state variables or when the setFormIsValid function is called.
useEffect(() => {
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
}, [setFormIsValid, enteredEmail, enteredPassword]);
What to and what not to add as useEffect() dependencies
That is correct, but there are a few exceptions you should be aware of:
setFormIsValid): React guarantees that those functions never change, hence you don’t need to add them as dependencies (you could though)fetch(), localStorage etc (functions and features built-into the browser and hence available globally): These browser APIs / global functions are not related to the React component render cycle and they also never changeimport { useEffect, useState } from "react";
let myTimer;
const MyComponent = (props) => {
const [timerIsActive, setTimerIsActive] = useState(false);
const { timerDuration } = props; // using destructuring to pull out specific props values
useEffect(() => {
if (!timerIsActive) {
setTimerIsActive(true);
myTimer = setTimeout(() => {
setTimerIsActive(false);
}, timerDuration);
}
}, [timerIsActive, timerDuration]);
};
In this example:
timerIsActive is added as a dependency because it’s component state that may change when the component changes (e.g. because the state was updated)timerDuration is added as a dependency because it’s a prop value of that component - so it may change if a parent component changes that value (causing this MyComponent component to re-render as well)setTimerIsActive is NOT added as a dependency because it’s that exception: State updating functions could be added but don’t have to be added since React guarantees that the functions themselves never changemyTimer is NOT added as a dependency because it’s not a component-internal variable (i.e. not some state or a prop value) - it’s defined outside of the component and changing it (no matter where) wouldn’t cause the component to be re-evaluatedsetTimeout is NOT added as a dependency because it’s a built-in API (built-into the browser) - it’s independent from React and your components, it doesn’t change
Debouncing is a technique used to improve performance by limiting the rate at which a function gets invoked. It is a common practice to improve the performance of input handlers. For example, if you have a search input field, you can debounce the onChange event handler to avoid making an API call on every keystroke. Instead, you can make the API call only after the user has stopped typing for a certain amount of time.
const validateEmailHandler = () => {
setEmailIsValid(enteredEmail.includes("@"));
};
const validatePasswordHandler = () => {
setPasswordIsValid(enteredPassword.trim().length > 6);
};
in each of the cases above we are setting a state variable based on the value of another state variable. This is a dependency and it can lead to bugs. For example, if we have a button that triggers the validation of both the email and password, and we click the button twice, the second time the validation will be wrong because the state variables will have changed in the meantime. This is a problem that useReducer() can solve.
useReducer() Syntax
const [state, dispatchFn] = useReducer(reducerFn, initialState, initFn);
(prevState, action) => newState;
import React, { useState, useEffect, useReducer } from "react";
import Card from "../UI/Card/Card";
import classes from "./Login.module.css";
import Button from "../UI/Button/Button";
const Login = (props) => {
const [enteredEmail, setEnteredEmail] = useState("");
const [emailIsValid, setEmailIsValid] = useState();
const [enteredPassword, setEnteredPassword] = useState("");
const [passwordIsValid, setPasswordIsValid] = useState();
const [formIsValid, setFormIsValid] = useState(false);
useEffect(() => {
console.log("EFFECT RUNNING");
return () => {
console.log("EFFECT CLEANUP");
};
}, []);
useEffect(() => {
const identifier = setTimeout(() => {
console.log("Checking form validity!");
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
}, 500);
return () => {
console.log("CLEANUP");
clearTimeout(identifier);
};
}, [enteredEmail, enteredPassword]);
const emailChangeHandler = (event) => {
setEnteredEmail(event.target.value);
};
const passwordChangeHandler = (event) => {
setEnteredPassword(event.target.value);
};
const validateEmailHandler = () => {
setEmailIsValid(enteredEmail.includes("@"));
};
const validatePasswordHandler = () => {
setPasswordIsValid(enteredPassword.trim().length > 6);
};
const submitHandler = (event) => {
event.preventDefault();
props.onLogin(enteredEmail, enteredPassword);
};
return (
<Card className={classes.login}>
<form onSubmit={submitHandler}>
<div
className={`${classes.control} ${
emailIsValid === false ? classes.invalid : ""
}`}
>
<label htmlFor="email">E-Mail</label>
<input
type="email"
id="email"
value={enteredEmail}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>
</div>
<div
className={`${classes.control} ${
passwordIsValid === false ? classes.invalid : ""
}`}
>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={enteredPassword}
onChange={passwordChangeHandler}
onBlur={validatePasswordHandler}
/>
</div>
<div className={classes.actions}>
<Button type="submit" className={classes.btn} disabled={!formIsValid}>
Login
</Button>
</div>
</form>
</Card>
);
};
export default Login;
import React, { useState, useEffect, useReducer } from "react";
import Card from "../UI/Card/Card";
import classes from "./Login.module.css";
import Button from "../UI/Button/Button";
const emailReducer = (state, action) => {
if (action.type === "USER_INPUT") {
return { value: action.val, isValid: action.val.includes("@") };
}
if (action.type === "INPUT_BLUR") {
return { value: state.value, isValid: state.value.includes("@") };
}
return { value: "", isValid: false };
};
const Login = (props) => {
// const [enteredEmail, setEnteredEmail] = useState('');
// const [emailIsValid, setEmailIsValid] = useState();
const [enteredPassword, setEnteredPassword] = useState("");
const [passwordIsValid, setPasswordIsValid] = useState();
const [formIsValid, setFormIsValid] = useState(false);
const [emailState, dispatchEmail] = useReducer(emailReducer, {
value: "",
isValid: null,
});
useEffect(() => {
console.log("EFFECT RUNNING");
return () => {
console.log("EFFECT CLEANUP");
};
}, []);
// useEffect(() => {
// const identifier = setTimeout(() => {
// console.log('Checking form validity!');
// setFormIsValid(
// enteredEmail.includes('@') && enteredPassword.trim().length > 6
// );
// }, 500);
// return () => {
// console.log('CLEANUP');
// clearTimeout(identifier);
// };
// }, [enteredEmail, enteredPassword]);
const emailChangeHandler = (event) => {
dispatchEmail({ type: "USER_INPUT", val: event.target.value });
setFormIsValid(
event.target.value.includes("@") && enteredPassword.trim().length > 6
);
};
const passwordChangeHandler = (event) => {
setEnteredPassword(event.target.value);
setFormIsValid(emailState.isValid && event.target.value.trim().length > 6);
};
const validateEmailHandler = () => {
dispatchEmail({ type: "INPUT_BLUR" });
};
const validatePasswordHandler = () => {
setPasswordIsValid(enteredPassword.trim().length > 6);
};
const submitHandler = (event) => {
event.preventDefault();
props.onLogin(emailState.value, enteredPassword);
};
return (
<Card className={classes.login}>
<form onSubmit={submitHandler}>
<div
className={`${classes.control} ${
emailState.isValid === false ? classes.invalid : ""
}`}
>
<label htmlFor="email">E-Mail</label>
<input
type="email"
id="email"
value={emailState.value}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>
</div>
<div
className={`${classes.control} ${
passwordIsValid === false ? classes.invalid : ""
}`}
>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={enteredPassword}
onChange={passwordChangeHandler}
onBlur={validatePasswordHandler}
/>
</div>
<div className={classes.actions}>
<Button type="submit" className={classes.btn} disabled={!formIsValid}>
Login
</Button>
</div>
</form>
</Card>
);
};
export default Login;
useEffect(() => {
const identifier = setTimeout(() => {
console.log("Checking form validity!");
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
}, 500);
return () => {
console.log("Cleanup");
clearTimeout();
};
}, [enteredEmail, enteredPassword]);
In the following code…
const Login = (props) => {
const [enteredEmail, setEnteredEmail] = useState("");
const [emailIsValid, setEmailIsValid] = useState();
const [enteredPassword, setEnteredPassword] = useState("");
const [passwordIsValid, setPasswordIsValid] = useState();
const [formIsValid, setFormIsValid] = useState(false);
we can use useReducer to combine the state and the state update logic for the entered email and the emailIsValid state… as well as combining the enteredPassword and passwordIsValid state.
const [enteredEmail, setEnteredEmail] = useState("");
const [emailIsValid, setEmailIsValid] = useState();
const [enteredPassword, setEnteredPassword] = useState("");
const [passwordIsValid, setPasswordIsValid] = useState();
Previously, we used object destructuring to add object properties as dependencies to useEffect().
const { someProperty } = someObject;
useEffect(() => {
// code that only uses someProperty ...
}, [someProperty]);
This is a very common pattern and approach, which is why I typically use it and why I show it here (I will keep on using it throughout the course).
I just want to point out, that they key thing is NOT that we use destructuring but that we pass specific properties instead of the entire object as a dependency.
We could also write this code and it would work in the same way.
useEffect(() => {
// code that only uses someProperty ...
}, [someObject.someProperty]);
This works just fine as well!
But you should avoid this code:
useEffect(() => {
// code that only uses someProperty ...
}, [someObject]);
Why? …
Because now the effect function would re-run whenever ANY property of someObject changes - not just the one property (someProperty in the above example) our effect might depend on.
| useState | useReducer |
|---|---|
| the main state managment tool | need more power (complex state like objects) |
| great for independent pieces of state | use if you have state that is related to other state |
| great for if state updates are simple and few |
With the current implementation of MealItemForm, every MealItem <Input /> receives the same id, as I do the following in the code I show in the previous lecture:
<Input
label="Amount"
input={{
id: "amount",
type: "number",
min: "1",
max: "5",
step: "1",
defaultValue: "1",
}}
/>
This works but it has two major disadvantages which are not immediately obvious (and hence unfortunately slipped through during the recordings):
id prop on the MealItemForm component and use that to create a unique id per <Input />:<Input
label="Amount"
input={{
id: "amount_" + props.id, // this changed!
type: "number",
min: "1",
max: "5",
step: "1",
defaultValue: "1",
}}
/>
We just have to make sure that the id props is passed correctly to <MealItemForm /> when that component is being used (i.e. inside of the MealItem component):
<MealItemForm id={props.id} />
Last but not least, for that to work, we should also pass id as a prop to MealItem, hence inside of the AvailableMeals component, we should create <MealItem /> elements like this:
<MealItem
id={meal.id} // this is new!
key={meal.id}
name={meal.name}
description={meal.description}
price={meal.price}
/>
import React from "react";
import classes from "./Modal.module.css";
const Backdrop = (props) => {
return <div className={classes.backdrop} />;
};
const ModalOverlay = (props) => {
<div className={classes.modal}>
<div className={classes.content}>{props.children}</div>
</div>;
};
const Modal = (props) => {
return (
<React.Fragment>
<Backdrop />
<ModalOverlay>{props.children}</ModalOverlay>
</React.Fragment>
);
};
export default Modal;
This is how we do the modal overlay with the react portal:
import React from "react";
import ReactDOM from "react-dom";
import classes from "./Modal.module.css";
const Backdrop = (props) => {
return <div className={classes.backdrop} />;
};
const ModalOverlay = (props) => {
<div className={classes.modal}>
<div className={classes.content}>{props.children}</div>
</div>;
};
const portalElement = document.getElementById("overlays");
const Modal = (props) => {
return (
<React.Fragment>
{ReactDOM.createPortal(<Backdrop />, portalElement)}
{ReactDOM.createPortal(
<ModalOverlay>{props.children}</ModalOverlay>,
portalElement
)}
</React.Fragment>
);
};
export default Modal;
Context(component-wide data) ⇓(Props…data from parent component)=======>(React Component)=======>(DOM…what the user sees)
⇑
State(internal data)
React uses a concept called the Virtual Dom which determines how the component tree (every component has a subtree) looks like and what it should look like when state changes
it should be noted that re-evaluating a component is not the same thing as re-rendering the dom.
import React, { useState } from "react";
import Button from "./components/UI/Button/Button";
import "./App.css";
function App() {
const [showParagraph, setShowParagraph] = useState(false);
const toggleParagraphHandler = () => {
setShowParagraph(!showParagraph);
};
return (
<div className="app">
<h1>Hi there!</h1>
{showParagraph && <p>This is new!</p>}
<Button onClick={toggleParagraphHandler}>Toggle Paragraph!</Button>
</div>
);
}
export default App;
import React, { useState } from "react";
import Button from "./components/UI/Button/Button";
import "./App.css";
function App() {
const [showParagraph, setShowParagraph] = useState(false);
const toggleParagraphHandler = () => {
setShowParagraph((prevShowParagraph) => !prevShowParagraph);
};
return (
<div className="app">
<h1>Hi there!</h1>
{showParagraph && <p>This is new!</p>}
<Button onClick={toggleParagraphHandler}>Toggle Paragraph!</Button>
</div>
);
}
export default App;
The component that has state or context that changes will be re-evaluated and possibly re-rendered whenever state changes
The exicution of a parent component will trigger the re-evaluation of all of its child components… if the state and props going to the child component do not change this will not trigger a change in the DOM
import React from "react";
import MyParagraph from "./MyParagraph";
const DemoOutput = (props) => {
console.log("DemoOutput RUNNING");
return <MyParagraph>{props.show ? "This is new!" : ""}</MyParagraph>;
};
export default React.memo(DemoOutput);
setSomething('new state value'') react will not immediately update the state and re-evaluate the component tree… Instead it will schedule a state update with the provided data.for this section we will be using the Star Wars API: https://swapi.dev/
function fetchMoviesHandler() {
fetch("https://swapi.dev/api/films/", {
method: "GET",
});
}
1.) Only call React Hooks in React Functions
1.) Custom hooks must start with the word “use”
i.e.
import { useState, useEffect } from "react";
const useCounter = () => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCounter((prevCounter) => prevCounter + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return counter;
};
export default useCounter;
Applied:
import useCounter from "../hooks/use-counter";
import Card from "./Card";
const ForwardCounter = () => {
const counter = useCounter();
return <Card>{counter}</Card>;
};
export default ForwardCounter;
We have 3 main kinds of State:
Why use Redux if we have React Context?
Reducers must be pure, side-effect free, synchronous functions
input (old state + action) -> output (new state)
This is the same way the useReducer() hook works
Where should side effects and async tasks be exicuted? > Inside the components (e.g. useEffect()) OR Inside the action creators (e.g. redux-thunk)
const Cart = ( props ) => {
const cartItems = useSelector((state) => state.cart.items);
{
cartItems.map((item) => (
<CartItem
item={{
title: item.title,
quantity: item.quantity,
total: item.totalPrice,
price: item.price,
}}
/>
));
}
Note: reducers cannot handle side effects or async tasks
A thunk is a function that wraps an expression to delay its evaluation.
Thunks are typically used to delay the evaluation of an expression until its results are needed, such as the dispatching of an action, or the calculation of derived data.
When building complex user interfaces we typically build single page applications (only one initial html request & response)
absolute path: starts with a forward slash (i.e. /products) and follows the domain name (i.e. https://mydomain.com/products)
relative path: starts with a dot (i.e. ./products) and follows the current path (i.e. https://mydomain.com/products/123)
import classes from "./NewsletterSignup.module.css";
import { useFetcher } from "react-router-dom";
function NewsletterSignup() {
const fetcher = useFetcher();
return (
/*Fetcher.Form will still trigger an action without initializing a route transition */
<fetcher.Form method="post" action="/newsletter" className={classes.newsletter}>
<input
type="email"
placeholder="Sign up for newsletter..."
aria-label="Sign up for newsletter"
/>
<button>Sign up</button>
</fetcher.Form>
);
}
export default NewsletterSignup;
In the code above useFetcher is the hook you should use if you want to trigger an action or loader without navigating to a different route"
Query Parameters