TOC
React
npx create-react-app [app-name]
cd [app-name] npm start
Default folder structure
react-app |----node_modules | |... |----public | |... |----src | |----App.js | |----index.js | | ... |----package.json |...
className
attribute in the JSX syntaxclass names should be in camelCase
Components
function Header() { return ( <h1>Hello World!</h1> ); } function App() { return <Header />; } export default App;
All components should start with uppercase
JSX
is short for the extended syntax for JavaScript. Components is a function that returns jsx
.For React code to be understood by a browser, you need to have a transpiling step in which the JSX code gets converted to plain JavaScript code that a modern browser can work with.
Export and Import Module
default export
vs. namedexport
named export
export AComponent
import { AComponent } from './A' // the only working importing manner
default export
can only appear once in each .js
file, and it supports us to assign a different name when importing the exported component
export default AComponent
import A from './A' import MyA from './A' import Something from './A'
Combine two import manner together
export default AComponent export X export Something
import A, { X as myX, Something } from './A'
React property objects: props
Parent component can send data to child components with props
import Heading from './components/Heading'; function App() { return ( <Heading firstName="Ethan"/> ); }
function Heading(props) { console.log(props); return ( <h1>This is an h1 heading saying {props.firstName}</h1> ); } export default Heading;
It’s a pure function so we can’t modify
props
within child componentsUsing transpiling we can see how the props actually pass to children
React.createElement( type, [props], [...children] )
JSX syntax
JSX is very expressive that merges JS, HTML, and CSS code together
Within curly braces we can write JavaScript code
props.children
function Greet(props) { console.log(props); const greet = { padding: "20px", border: "1px solid gray", background: "#fff", margin: "20px 0" } return ( <div style={greet}> {props.friends} </div> ); } function GreetFirst(props) { console.log(props); return ( <h1>Greet from {props.firstName}</h1> ); } function GreetLast(props) { console.log(props); return ( <h1>Greet from {props.lastName}</h1> ); } export default Greet; export { GreetFirst, GreetLast };
import Greet, { GreetFirst, GreetLast } from './components/Greeting'; function App() { return ( <div className="App"> <Greet friends={<GreetFirst firstName="Ethan" />} /> {/* This works */} <Greet> <GreetFirst firstName="Ethan" /> {/* They are using props.children */} <GreetLast lastName="Wang" /> </Greet> </div> ); } export default App;
Hooks
state hook
import { useState } from 'react'; function ModeToggler() { const [mode, setMode] = useState('light'); const toggleHandler = () => { mode === 'light' ? setMode('dark') : setMode('light'); console.log('mode:', mode); }; return ( <button onClick={toggleHandler}> {mode === 'light' ? 'Light Mode': 'Dark Mode'} </button> ); } export default ModeToggler;
When using
useState
with object, it’s recommended to copy the state object and then update the copy because we cannot reassign a variable declared using const
import { useState } from "react"; export default function App() { const [greeting, setGreeting] = useState({ greet: "Hello, World" }); console.log(greeting, setGreeting); function updateGreeting() { const newGreeting = {...greeting}; newGreeting.greet = "Hello, World-Wide Web"; setGreeting(newGreeting); } return ( <div> <h1>{greeting.greet}</h1> <button onClick={updateGreeting}>Update greeting</button> </div> ); }
ref hook
import { useState, useRef } from 'react'; export default function InputComponent() { const [inputText, setText] = useState('hello'); function handleChange(e) { setText(e.target.value); } return ( <> <input value={inputText} onChange={handleChange} /> <p>You typed: {inputText}</p> <button onClick={() => setText('hello')}> Reset </button> </> ); } export function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { // `current` points to the mounted text input element inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); }
effect hook
State
Stateful vs. Stateless
Ref
Context
provide global state
import { createContext, useContext, useState } from "react"; const UserContext = createContext(undefined); export const UserProvider = ({children}) => { const [user] = useState({ name: "Ethan", email: "yihao.w@nyu.edu", }); return ( <UserContext.Provider value={{user}}> {children} </UserContext.Provider> ) } export const useUser = () => useContext(UserContext);
import { useUser } from "../UserContext"; const LoggedInUser = () => { const {user} = useUser(); return ( <div> <h1>Welcome, {user.name}!</h1> </div> ) } const Header = () => { return ( <header> <LoggedInUser /> </header> ) } function BlogPage() { const {user} = useUser(); return ( <div> <Header /> <h2>Blog</h2> <p>Here's a blog post! You are logged in as {user.name}.</p> </div> ) } export default BlogPage;
import './App.css'; import Nav from './components/Nav'; import { UserProvider } from './components/UserContext'; function App() { return ( <Nav /> ); } function Root() { return ( <UserProvider> <App /> </UserProvider> ); } export default Root;
It’s better to stick to
props
and states, because their data flows are easier to manage and followRoute
npm install react-router-dom
Create a SPA(single page application)
import Homepage from './Homepage'; import Test from './Test'; import About from './components/About'; import { Link, Route, Routes } from 'react-router-dom'; function App() { return ( <div> <nav className="main-nav"> <Link to="/">Home</Link> <br /> <Link to="/about">About</Link> <br /> <Link to="/test">Test</Link> </nav> <Routes> <Route path="/" element={<Homepage />} /> <Route path="/about" element={<About />} /> <Route path="/test" element={<Test />} /> </Routes> </div> ); } export default App;
import { BrowserRouter } from 'react-router-dom' render( <BrowserRouter> <App /> </BrowserRouter>, document.getElementById('root') )
re-renders
const App = () => { const a = 'hi'; const b = 'bye'; const value = useMemo(() => ({a, b}), [a, b]); return ( <AppContext.Provider value={value}> <ComponentA /> </AppContext.Provider> ); }; const ComponentA = React.memo(() => <ComponentB />); const ComponentB = () => <ComponentC />; const ComponentC = () => { const contextValue = useContext(AppContext); return null; };
Some useful components
Images
import me from '../../assets/square-1MB.png'; function About() { return ( <div> <h1>About</h1> <p>This is the about page</p> <img src={me} width={200} alt="Me" /> </div> ); } export default About;
Videos
import ReactPlayer from "react-player"; const MyVideo = () => { const vidURL = 'https://www.youtube.com/watch?v=jRr6WGRTekI'; return ( <ReactPlayer url={vidURL} /> ); }; export default MyVideo;
Audios
List
Form
import { useState } from "react"; function Form() { const [name, setName] = useState(''); const handleSubmit = (e) => { e.preventDefault(); setName(''); console.log('Form submitted'); } return ( <form onSubmit={handleSubmit}> <input type="text" placeholder="Name" name="name" value={name} onChange={(e) => setName(e.target.value)} /> <button type="submit" disabled={!name}> Submit </button> </form> ) } export default Form;
formik
jaredpalmer • Updated Feb 11, 2024
Test
import { fireEvent, render, screen } from "@testing-library/react"; import FeedbackForm from "./FeedbackForm"; describe("Feedback Form", () => { test("User is able to submit the form if the score is lower than 5 and additional feedback is provided", () => { const score = "3"; const comment = "The pizza crust was too thick"; const handleSubmit = jest.fn(); render(<FeedbackForm onSubmit={handleSubmit} />); const rangeInput = screen.getByLabelText(/Score:/); fireEvent.change(rangeInput, { target: { value: score } }); const commentText = screen.getByLabelText(/Comments:/); fireEvent.change(commentText, { target: { value: comment } }); const submitButton = screen.getByRole("button"); fireEvent.click(submitButton); expect(handleSubmit).toHaveBeenCalledWith({ score, comment, }); }); test("User is able to submit the form if the score is higher than 5, without additional feedback", () => { const score = "9"; const handleSubmit = jest.fn(); render(<FeedbackForm onSubmit={handleSubmit} />); const rangeInput = screen.getByLabelText(/Score:/); fireEvent.change(rangeInput, { target: { value: score } }); const submitButton = screen.getByRole("button"); fireEvent.click(submitButton); expect(handleSubmit).toHaveBeenCalledWith({ score, comment: "", }); }); });