Fret not my friend, now you no longer need to stumble around in the dark! This article is a memory aid for all things React (focusing on Functional Components only).
Create a React App
// Initialize a new app
npx create-react-app my-app-name
// or
yarn create react-app my-app-name
// Run the app (default port is 3000)
cd my-app-name
npm start
// or
yarn start
Render a React Component
import ReactDOM from "react-dom/client";
import App from "./App";
// ...
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
Functional Components
const Component = () => {
return <div>Hello World</div>;
};
Pre-requisites:
- Must have an uppercase first letter
- Must return JSX
Since React 17, there is no need to import React from 'react'
Importing Components
Components
can be exported & imported from other files, thus promoting Code splitting and reusability.
Default Export
function Component = () =>
<div>Hello World</div>
export default Component
and in another file:
import Component from './Component'
function App = () => <Component />
Named Export
export function Component = () =>
<div>Hello World</div>
and in another file:
import { Component } from './Component'
function App = () => <Component />
Lazy Loading
function Component = () =>
<div>Hello World</div>
export default Component
and in another file:
import { lazy, Suspense } from 'react'
const Component = lazy(() => import('./Component'))
function App = () => (
<Suspense fallback={<div>Loading...</div>}>
<Component />
</Suspense>
)
JSX Rules
1. Must return a single element
const Component = () => {
// INVALID: return <div>Hello</div><div>World</div>;
return <div>Hello World</div>;
};
// or
const Component = () => {
// `<React.Fragment>` can be replaced with just `<>`
// On wrapping the children with any element,
// you can create as many levels of nesting as you want
return (
<React.Fragment>
<div>Hello</div>
<div>World</div>
</React.Fragment>
);
};
2. Opening tags need to be closed (can use self-closing tags)
const Component = () => {
// INVALID: return <input>;
return <input />;
};
3. Use React attributes instead of HTML attributes
const Component = () => {
// INVALID: return <div class="foo">Hello World</div>;
return <div className="foo">Hello World</div>;
};
Styling
To use styling you do need to add css-loader
& style-loader
to your webpack.config.js
if you are manually building your React app. Luckily, create-react-app
comes pre-configured to enable styling.
CSS Imports
/* app.css */
.redText {
color: red;
}
import "./app.css";
function App() {
return <h1 className="redText">
Hello World
</h1>;
}
Inline CSS
const Component = () => {
return <div style={{ color: "red" }}>
Hello World
</div>;
};
CSS Modules
/* app.css */
.redText {
color: red;
}
import classes from "./app.css";
function App() {
return <h1 className={classes.redText}>
Hello World
</h1>;
}
Embedding JavaScript
Prerequisites:
- Must be an expression with a return value (can be JSX too)
- Must be wrapped in curly braces (
{}
)
const Component = () => {
const isLoggedIn = true;
return <div>
{isLoggedIn ? "User is Authenticated" : <LogIn />}
</div>;
};
Component Properties
These are the values with which the component is initialized. props
are accepted as the function parameter.
// no props
function App() {
return <Person name="Mike" age={29} />;
}
// with props
const Person = (props) => {
return (
<h1>
Name: {props.name}, Age: {props.age}
</h1>
);
};
// with destructured props
const Person = ({ name, age }) => {
return (
<h1>
Name: {name} Age: {age}
</h1>
);
};
Children
children
is a special prop
passed to a component that is rendered inside the component.
const Component = ({ children }) => {
return <div>{children}</div>;
};
const App = () => {
return (
<Component>
<h1>Hello World</h1>
</Component>
);
};
Default Props
// JavaScript-ish syntax
const Component = ({ name = "Mike" }) => {
return <div> {name} </div>;
};
// or React-ish syntax
const Component = ({ name }) => {
return <div> {name} </div>;
};
Component.defaultProps = {
name: "Mike",
};
Lists
const people = [
{ id: 1, name: "Mike" },
{ id: 2, name: "Peter" },
{ id: 3, name: "John" },
];
function App() {
return people.map((person) => (
<div key={person.id}>{person.name}</div>;
));
}
The key
is an optional prop
available on all elements, it is used internally by React to keep track of which elements have changed. For lists, it is highly recommended that you do add a key
.
Prop Destructuring
Person
is a component that accepts a name
prop.
function App() {
return people.map(({id, ...person}) => (
<Person key={id} {...person} />;
));
}
Events
const clickHandler = () => alert("Hello World");
function App() {
return (
<>
<h1>Welcome to my app</h1>
<button onClick={clickHandler}>
Say Hi
</button>
</>
);
}
or inline...
function App() {
return (
<>
<h1>Welcome to my app</h1>
<button onClick={() => alert("Hello World")}>
Say Hi
</button>
</>
);
}
We can also pass arguments to the handler
const clickHandler = (message) => alert(message);
function App() {
return (
<>
<h1>Welcome to my app</h1>
<button onClick={() => clickHandler("Hello World")}>
Say Hi
</button>
</>
);
}
The events by default pass the event object as the first argument.
const clickHandler = (event) => console.log(event.target);
function App() {
return (
<>
<h1>Welcome to my app</h1>
<button onClick={clickHandler}>
Say Hi
</button>
</>
);
}
You can even pass on a handler from a parent and execute it inside the child
function Todo({item, onDelete}) {
return (
<div>
{item}
<button onClick={() => onDelete(item)} />
</div>
)
}
function Todos() {
const handleDelete = (todo) => {
const newTodos = todos.filter(item => item !== todo)
setTodos(() => newTodos)
}
return (
{todos.map((todo) => (
<Todo item={todo} onDelete={handleDelete}/>
))}
)
}
Hooks
Hooks are functions that let you “hook into” React state and lifecycle features from function components.
Prerequisites:
- Hook always starts with the 'use' prefix
- Must be invoked only in a React functional component
- Must be called only at the top level of a functional component
- Declaration CAN NOT be called conditionally
useState
useState
is a hook that lets you manage the state in a functional component.
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
useEffect
useEffect
is a hook that lets you access lifecycle methods in a functional component.
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Initialized");
// clean up function runs before the component is unmounted
return () => {
console.log("Cleaned up");
};
}, []); // empty array: run during mount only
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // array with count: run everytime `count` changes
return (
<div>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
useContext
useContext
is a hook that returns the data for the given context
(the state management tool that ships with React)
const ThemeContext = createContext("light");
function App() {
return (
<ThemeContext.Provider value="light">
<Component />
</ThemeContext.Provider>
);
}
function Component() {
const theme = useContext(ThemeContext); // returns 'light'
return (
<div>
<p>The current theme is: {theme}</p>
</div>
);
}
useReducer
useReducer
is a hook that lets you manage state in functional components, but unlike useState
it uses the Redux pattern
function App() {
const [count, dispatch] = useReducer((state, action) => {
switch (action) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
throw new Error();
}
}, 0);
return (
<div>
<p>{count}</p>
<button onClick={() => dispatch("increment")}>
+
</button>
<button onClick={() => dispatch("decrement")}>
-
</button>
</div>
);
}
useCallback
The useCallback
hook returns a memoized version of the callback, with the sole purpose of optimizing performance.
function App() {
const [count, setCount] = useState(0);
const increment = useCallback(() =>
setCount((c) => c + 1), []);
return (
<div>
<p>{count}</p>
<button onClick={increment}>+</button>
</div>
);
}
useMemo
The useMemo
hook returns a memoized version of the value produced by the callback. Just like useCallback
, useMemo
is a performance optimization hook.
function App() {
const [count, setCount] = useState(0);
const memoizedIncrement = useMemo(() => {
return () => setCount((c) => c + 1);
}, []);
return (
<div>
<p>{count}</p>
<button onClick={memoizedIncrement}>+</button>
</div>
);
}
useRef
The useRef
hook returns a mutable ref object
whose .current
property is initialized to the passed argument (initialValue
). The returned object will persist for the full lifetime of the component unless manually changed.
function App() {
const inputRef = useRef(null);
const onButtonClick = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={onButtonClick}>
Focus on the input
</button>
</div>
);
}
useTransition
The useTransition
hook lets you mark less-urgent actions as transitions.
function App() {
const [input, setInput] = useState("");
const [data, setData] = useState([...items]);
const [isPending, startTransition] = useTransition();
useEffect(() => {
// input change is prioritized over filtering a long list
startTransition(() => {
setData(items.filter((i) => i.includes(input)));
});
}, [input]);
const updateInput = (e) => setInput(e.target.value);
return (
<div>
<input value={input} onChange={updateInput} />
<ul>
{data.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
useDeferredValue
The useDeferredValue
hook lets you intentionally defer updating values so they don't slow down other parts of the page
function App() {
const deferredValue = useDeferredValue(value);
return <MyComponent value={deferredValue} />;
}
That's all folks! If you think I have missed something, please add them in the comments 👇
Happy Developing!