- JSX produces React elements
-
"React embraces the fact that rendering logic is inherently coupled with other UI logic: how events are handled, how the state changes over time, and how the data is prepared for display."
- Separation of concerns by components that contain Markup and Logic (suck it, Angular!)
- React doesn't require JSX
- JSX is an expression (can be used in IF / FOR)
- Quotes XOR Curly Braces
- JSX prevents injection attacks e.g. XSS
- JSX is compiled by Babel to vanilla JS (
React.createElement()
calls) => React Element - Element != Component
- React Element = plain objects (cheap to create) != DOM Element
- React DOM = Virtual DOM
- Single root DOM node => everything inside managed by React DOM
- Multiple isolated root DOM nodes => multiple isolated React apps on the same page
ReactDOM.render(element, document.getElementById('root'))
- React elements are immutable (all hail Functional Programming!)
-
"React DOM compares the element and its children to the previous one, and only applies the DOM updates necessary to bring the DOM to the desired state."
- Components => splitting the code into reusable, isolated pieces
- Component =
(props) => ReactElement
- Component = Function or Class component
- Component names always start with CAPITAL letter
- Don't be afraid to extract components / split components into smaller components
- Props are immutable (again, FP)
- All React components MUST act as pure functions
- State:
- Similar to props
- Private
- Fully controlled by the component
- Class Component: a class, extending
React.Component
, with arender()
method - State in Class Component: defined in
constructor(props) {}
- Class Components should always call the base constructor with props as argument
- Lifecycle methods:
componentDidMount()
- Runs after the component output has been rendered to the DOM
componentDidUpdate()
componentWillUnmount()
- Do not modify state directly (outside of constructor) => always use
setState(newState)
useState((state, props) => {/* Update state here */})
=> async state update- State updates are merged
- Data flows down = a component can share its state with its children
- React event are named using camelCase instead of lowercase – e.g.
onclick
=>onClick
- Methods used in JSX must be bound – can be mitigated by public class fields syntax (
xyz = () => {}
) or by arrow function in JSX* (* multiple re-renders when passed down) - For hiding components return
null
=> lifecycle methods will still fire <li>
elements should always have a unique (among siblings)key
prop- Indexes should not be keys
- Form elements => controlled components
- File input => uncontrolled component
- For forms just use Formik
- Lifting state up = Extracting shared state to closest common ancestor
- Composition > Inheritance
- Children elements can be passed by the special children prop: props.children
- Specialized components = wrappers for more generic component with extra configuration
- Single Responsibility Principle!!!
- provides a way to pass data through the component tree w/o having to pass props down manually at every level
- Context is designed to share data that can be considered global for a tree of React components
- Context is primarily used when some data needs to be accessible by many components at different nesting levels => it makes component reuse more difficult
const MyContext = React.createContext(defaultValue);
- When React renders a component that subscribes to this Context object, it will read the current context value from the closest matching Provider above it in the tree
- The
defaultValue
arg is only used when a component does not have a matching Provider above it in the tree
<MyContext.Provider value={/* Some value */} />
-
Every Context object comes with a Provider React component that allows consuming components to subscribe to context changes
-
Accepts a
value
prop to be passed to consuming components that are descendants of this Provider -
One Provider can be connected to many consumers
-
Providers can be nested to override values deeper within the tree
-
All consumers that are descendants of a Provider will re-render whenever the Provider's
value
prop changes -
The propagation from Provide to its descendant consumers (incl.
.contextType
anduseContext
) is not subject to theshouldComponentUpdate
method => the consumer is updated even when an ancestor component skips an update -
Binding context to a Class Component (only one context!):
class MyClass extends React.Component { static contextType = MyContext; render() { let value = this.context; return <h1>Value: {value}</h1>; } }
-
A React component that subscribes to context changes is a Consumer
<MyContext.Consumer>
{value => /* Render something based on the context value */}
</Mycontext.Consumer>
- This lets us subscribe to a context within a function component
- Requires a function as a child
- The function receives the current context value and returns a React node
- The
value
arg passed to the function will be equal to thevalue
prop of the closest Provider for this context above in the tree, or to thedefaultValue
arg that was passed tocreateContext()
MyContext.displayName
- it can override the name of the Context in DevTools
- A React Component cannot return multiple elements => Fragments solves this without adding extra nodes to the DOM
const MyComponent = () => (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
const Shorthand = () => (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
- new addition in React 16.8
- lets us use state and other React features without writing a class
- completely opt-in
- 100% backwards-compatible
- functions that let you hook into React state and lifecycle features from function components
- Hooks don't work inside classes
- React relies on the order in which the Hooks are called
- Hook Rulez:
- Only call Hooks at the top level - not inside loops, conditions or nested function
- Only call Hooks from React function components or from custom Hooks
- makes a function component stateful
- a function (returning the value) can be provided as initialValue if the value is the result of an expensive computation
const [myStateVar, setMyStateVar] = useState(initialValue);
- (Side) Effect = data fetching, subscriptions, manually changing the DOM from React components
- The Effect hook adds the ability to perform side effects from a function component
- It serves the same purpose as
componentDidMount
,componentDidUpdate
andcomponentWillUnmount
in React classes, but unified into a single API - has access to Component state and props
- triggers after the first render AND after every update
- The provided function can return a callback which will be the cleanup callback, and it will be executed upon component unmount
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Clicked ${count} times`;
// cleanup callback can be returned
// return () => {
// do the cleanup
//}
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
- lets us subscribe to React context without nesting
function Example() {
const theme = useContext(ThemeContext);
}
- lets us manage local state of complex components with a reducer
function Example() {
const [state, dispatch] = useReducer(myReducer, initState);
// myReducer = (state, action) => newState
}
// Simplified implementation
function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch(action) {
const nextState = reducer(state, action);
setState(nextState);
}
return [state, dispatch];
}
- Returns a memoized callback
- The memoized version of the callback only changes when one of the dependencies has changed
// useCallbacks = (fn, depList) => memoizedCallback
const memoizedCallback = useCallback(doSomething(a, b), [a, b]);
- returns a memoized value
- will only recompute the value when one of the dependencies has changed
- no dependencies => new value calculation on every render
// useMemo = (generator, depList) => memoizedValue
const memoizedValue = useMemo(computeExpensiveValue(a, b), [a, b]);
- 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
- consistent reference to the object
const refContainer = useRef(initialValue);
- customizes the instance value that is exposed to parent components when using
ref
useImperativeHandle(ref, createHandle, [...deps]);
- the signature is identical to useEffect
- fires synchronously after all DOM mutations
- used to read layout from DOM and re-render synchronously
- can be used to display a label for custom hooks in React DevTools
useDebugValue(value);
- A JS function
- its name starts with
use
- it may call other Hooks
- mechanism to reuse stateful logic
- doesn't share state
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
const handleStatusChange = ({ isOnline }) => setIsOnline(isOnline);
ChatAPI.subscribeToFriendStatus(frinedID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}