Skip to content

Instantly share code, notes, and snippets.

@tomiolah
Last active February 1, 2024 13:57
Show Gist options
  • Save tomiolah/a1c6395af2831a25eb3cbf853eabe014 to your computer and use it in GitHub Desktop.
Save tomiolah/a1c6395af2831a25eb3cbf853eabe014 to your computer and use it in GitHub Desktop.
React Starter Notes

Main Concepts

  • 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 a render() 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!!!

Advanced Guides (Context, Fragments)

Context

  • 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 and useContext) is not subject to the shouldComponentUpdate 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 the value prop of the closest Provider for this context above in the tree, or to the defaultValue arg that was passed to createContext()
  • MyContext.displayName - it can override the name of the Context in DevTools

Fragments

  • 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 />
  </>
);

HOOKS

  • 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

State Hook

  • 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);

Effect Hook

  • (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 and componentWillUnmount 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>
  );
}

Context Hook

  • lets us subscribe to React context without nesting
function Example() {
  const theme = useContext(ThemeContext);
}

Reducer Hook

  • 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];
}

Memoized Callback Hook

  • 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]);

Memoized Value Hook

  • 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]);

Reference 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
  • consistent reference to the object
const refContainer = useRef(initialValue);

Imperative Handle Hook

  • customizes the instance value that is exposed to parent components when using ref
useImperativeHandle(ref, createHandle, [...deps]);

Layout Effect Hook

  • the signature is identical to useEffect
  • fires synchronously after all DOM mutations
  • used to read layout from DOM and re-render synchronously

Debug Value Hook

  • can be used to display a label for custom hooks in React DevTools
useDebugValue(value);

Custom Hooks

  • 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment