React.js Reference Guide
Table of Contents generated with DocToc
Fundamentals
- React is simply a JS library
- Hello, world react app, using pure react, i.e. just some JS on a page; Run on JS Bin
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Adopt Me</title> </head> <body> <div id="root">not rendered</div> <script src="https://unpkg.com/react@16.8.4/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16.8.4/umd/react-dom.development.js"></script> <script> ReactDOM.render(React.createElement("h1", {}, "Hello, world!"), document.getElementById("root")); </script> </body> </html>
- Props are variables that a parent passes to its children
const Pet = props => { return React.createElement("div", {}, [ React.createElement("h1", {}, props.name), React.createElement("h2", {}, props.animal), React.createElement("h2", {}, props.breed) ]); }; const App = () => { return React.createElement("div", {}, [ React.createElement(Pet, { name: "Pepper", animal: "Bird", breed: "Cockatiel" }), React.createElement(Pet, { name: "Luna", animal: "Dog", breed: "Havanese" }) ]); }
- Components are of two types
- Class components
- Function components
- Function component
- Must return markup
- Must be pure, i.e. have not side effects
- Example of function component, Run on JS Bin
const App = (props) => { return React.createElement("h1", {}, props.message); }; ReactDOM.render(React.createElement(App, {message: "Hello, world!"}), document.getElementById("root"));
React.createElement
creates one instance of a component- In the example above,
App
is a class of components andReact.createElement
creates an instance of the class passed to it as argument
- Class component
- Extends
React.Component
- Have a render method that returns a react element
- Example of a class component, Run on JS Bin
class App extends React.Component { constructor(props) { super(props); this.state = {message: props.message.toUpperCase()} } render() { return <h1>{this.state.message} ; {this.props.message}</h1>; } }
- Provide state and lifecycle methods for the component
- State is private to the component
- A component may choose to pass its state down as props to its child components
<h1>It is {this.state.foo}.</h1>
- Extends
- All react component must act like pure functions with respect to their props
- Instead of pulling react library and other future dependencies from unpkg.com / similar CDN, it is better to download
it locally using npm and then package into the app bundle
shell script npm install react react-dom
- Packer can bundle all dependencies from import statements in the src files
- JSX (JavaScript XML) is a syntax extension to JS
- It is not HTML
- Produces React elements
- React doesn’t require JSX, but makes writing UI code inside JS easier to read/write
React.createElement("h1", { id: "main-title" }, "Hello, world!");
is equivalent to<h1 id="main-title">Hello, world!</h1>
- JS expressions can be used within
{}
inside JSX code<h1>{props.name}</h1>
- Handling HTML
form
elements- Whenever a DOM events happens, React thinks something may have changed and runs a re-render, overwriting any changes
to a input text field initiated by a user. Ref: Run on JS Bin
const SearchParams = () => { const location = "Seattle, WA"; return ( <div className="search-params"> <form> <label htmlFor="location"> Location <input id="location" value={location} placeholder="Location" /> </label> <button>Submit</button> </form> </div> ); };
- These elements need to be handled using Controlled Components
to allow a JS function to handle form submission and read the value of form elements beings submitted
- Event handler methods in a controlled component need to be explicitly bound to the object’s
this
because in JS, class methods are not bound by default as illustrates in MDN reference doc
- Event handler methods in a controlled component need to be explicitly bound to the object’s
- Alternatives to controlled components: uncontrolled component where the form data is handled by the DOM instead of in the react state object
- Whenever a DOM events happens, React thinks something may have changed and runs a re-render, overwriting any changes
to a input text field initiated by a user. Ref: Run on JS Bin
- Strict Mode can be enabled to highlight potential issues in a react app
- Reach Router is a new router from the creators of React Router
Error Boundaries
- Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed
- Only class components can be error boundaries
- Ref: https://reactjs.org/docs/error-boundaries.html
- Wrapping a component in an error boundary component
<ErrorBoundary> <MyWidget /> </ErrorBoundary>
- Alternative method to wrap a component in an error boundary
// Replace the following: export default MyComponent; // with export default function MyComponentErrorBoundary(props) { return ( <ErrorBoundary> <MyComponent {...props} /> </ErrorBoundary> ); }
Hooks
- Introduced in React 16.8
- Allow using / “hooking into” state and component lifecycle without writing classes, i.e. from function components
- Do not put hooks inside if statement or loops because hooks rely on strict ordering of declaration
- Hooks can’t be used in a class component
useState
hook: for handling form elements, i.e stateimport React, { useState } from "react"; const ExampleComponent = () => { const [location, setLocation] = useState("Seattle, WA"); return( <input id="location" value={location} placeholder="Location" onChange={e => setLocation(e.target.value)} /> )};
useEffect
hook: for hooking into component lifecycle methods that perform side effectsconst [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; });
useEffect
can be configured to perform cleanup, by returning what needs to be done as cleanup from theuseEffect
functionuseEffect(() => { const timer = setTimeout(setTime(new Date()), 1000); // similar to componentDidUnmount return () => clearTimeout(timer); });
useRef
returns a mutable reference object whose.current
property is initialised to the passed argumentuseRef
is useful as the returned object persis for the full lifetime of the component and thus its value persists across every render- Mutating the
.current
property does not cause a re-renderconst RefComponent = () => { const [stateNumber, setStateNumber] = useState(0); const numRef = useRef(0); function incrementAndDelayLogging() { setStateNumber(stateNumber + 1); numRef.current++; setTimeout( // value of stateNumber lags behind the value of numRef, as stateNumber holds previous value in its closure // before the re-render is trigerred () => alert(`state: ${stateNumber} | ref: ${numRef.current}`), 1000 ); } return ( <div> <h1>useRef Example</h1> <button onClick={incrementAndDelayLogging}>delay logging</button> <h4>state: {stateNumber}</h4> <h4>ref: {numRef.current}</h4> </div> ); };
- Ref: Run on codesandbox.io
useReducer
is an alternative touseState
which is usually preferable when you have state logic involving multiple sub-values or when the next state depends on the previous one or when it would improve code readability (and testability) to keep state update logic in one place (in the reducer function definition)function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, {count: 0}); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'decrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </> ); }
useMemo
returns a memoized value- Takes a create function and an array of dependencies as arguments and only recomputes the memoized value using the passed create function when one of the dependencies changes
- Helps avoid expensive calculations on every render
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Context
- Context provides a way to pass data through the component tree without having to pass props down manually at every level
- Using contexts
- Create context using
createContext
which returns an object with two React components in it: a Provider and a Consumer// A hook (array of state and function) is passed as argument to createContext const ThemeContext = React.createContext(["green", () => {}]);
- Pass shared context values by wrapping the required components in the Provider
import ThemeContext from "./ThemeContext"; const theme = useState("darkblue"); // wrap the rest of the app <ThemeContext.Provider value={theme}> […] </ThemeContext.Provider>
- Use the shared context value in the child components
- In a Function Component using
useContext
hookconst SearchParams = () => { // ... const [theme] = useContext(ThemeContext); // ... return ( <button style={ { backgroundColor: theme } }>Submit</button>; // ... ); }
- In a Class Component using
contextType
static contextType = ThemeContext; render () { // ... return ( // ... <button style={ { backgroundColor: this.context[0] } }> // ... ); }
- In a Class Component using
Content.Consumer
render () { // ... return ( <ThemeContext.Consumer> {value => ( // ... <button style={ { backgroundColor: value[0] } }> // ... )} </ThemeContext.Consumer> ); }
- In a Function Component using
- Create context using
- Ref: https://btholt.github.io/complete-intro-to-react-v5/context
- Ref: https://reactjs.org/docs/context.html
- Ref: https://reactjs.org/docs/hooks-reference.html#usecontext
Emotion
- A way to write CSS in JS
- An alternative to Styled Components
- Installation, including babel preset
npm install @emotion/core @emotion/babel-preset-css-prop
- Babel configuration: add the following to the
.babelrc
{ "presets": [ [ "@emotion/babel-preset-css-prop", { "sourceMap": false } ] ] }
References