React basics

React.js fundamentals

view on github

JSX Markup

  • vscode naturally detects and supports JSX syntax
  • react supports arrow functions (ie. components do not use this) ...
  • wrapping JSX in parens is a best practice that I agree with ...
  • remember that JSX is compiled before rendering weSmart
  • use double quotes when specifying attributes in JSX markup (not the same as HTML attributes)
  • here's the exhaustive JSX attributes list

Components and elements

  • elements are dynamic html like elements that are rendered in the virtual DOM
  • components are groups of elements and / or other components (App is the parent component of them all)
  • COMPONENTS NAMES MUST BEGIN WITH A CAPITAL LETTER OR REACTS TREATS THEM AS FLAT HTML ELEMENTS
  • it's better to name props from the component's point of view
  • all React components must act like pure functions with respect to their props :
    • pure functions do not change their parameter's values
    • pure functions always return the same value for the same input

Components states

  • component state encapsulates properties initialized in the constructor and accessed through this.state
  • conversely to props, state properties are meant to be modified as needed during the component's lifecycle
  • the state can be changed at any point during execution by calling the setState() method :
    • react will detect the state has changed and will render the component into the DOM again
    • updating the state directly is possible, but will not cause a render
    • modifying the previous state is possible with the following signature :
// the newState object is shallow merged into the previousState object, it doesn't owerwrite it
setState((previousState, props) => { ... return newState })
  • the data flows down : a state is owned by a single component and any data or UI deriving from it can only affect sub-components
  • as a result, only parent components can change sub-components props so as to affect how sub-components are rendered ...

Components lifecycle

  • React.Component has custom methods :
    • componentDidMount() : fires after the component output has been rendered to the DOM
    • componentWillUnmount() : fires as the component is removed from the DOM
  • components also can implement any custom property or method as needed, like regulare ECMA classes
  • react actually implements MVC : model with component state, view with component.render() and controller with component.setState()

Events

  • JSX does not support addEventListener, callbacks are directly passed as an attribute of a JSX component
   <x onClick={ someFunction } />
  • how to pass dynamic parameters to a callback instead of binding in the component's constructor :
<x onClick={ someFunction.bind(this, param) } />
  • callback signature is the same as ECMA, however preventDefault() is the only way to prevent the default behavior
  • react events are "synthetic events" (wrappers around native DOM events)

Lists and keys

  • JSX allows the rendering of dynamic lists of components :
    • any method of Array fits this purpose
    • components lists must be wrapped inside another component since render() must return a single component
    • inside a components list, each component must have a unique key string attribute
    • unique ids from data maybe used as keys (or generated on the fly weSmart)
    • the "item index" value is available as a key as a fallback, but its use is strongly discouraged
    • keys must be unique for the current list, and are not passed to the component itself (not accessible from inside the component)

Forms

  • storing any user input in a component's state is called "controlled component" behavior
  • the state is usually updated and the component re-rendered at each input, making react the "single source of truth"
  • some JSX tags differ from the HTML standard in order to support "controlled component" :
    • <select value={this.state.selected}>...</select> (so it can behave like a text input)
  • some points to consider :
    • either writing a dedicated handler for each input or handle multiple inputs in a single handler
    • setting the value attribute on a controlled component makes it read-only (use defaultValue to initialize at each rendering)
    • <input type="file" /> cannot be included in a controlled component because its value is read-only
    • uncontrolled components may then be an alternative

State lifting

  • when the rendering of multiple components depend on the same data, said data can't be stored in the component's states :
    • instead, it has to be moved up to the state of their closest common ancestor and passed to them as props
    • the rendering of the ancestor will then trigger their own rendering, this is called "lifting the state"
    • NEVER TRY TO SYNC STATE ON DIFFERENT COMPONENTS AND RELY ON TOP-DOWN DATA FLOW INSTEAD
    • IF A VALUE CAN BE DERIVED FROM PROPS OR STATE, THEN IT SHOULDN'T BE IN THE STATE
  • react developer tools can be used to debug component's states in the code inspector

Composition and inheritance

  • components can be used as self-closing tags or enclosing tags for any other component / element they should display (cf. BogusButton)
  • special property props.children allows creating "generic" components that can be dynamically reused in other components (example : styled boxes)
  • "composition" components are specific components which render more generic ones and pass them props (cf. PepeRandom)
  • classes inheritance is strongly discouraged in react : AS A RESULT, EVERY COMPONENT CLASS EXTENDS React.Component

Thinking in React (excerpts)

✔️ react makes you think about apps as you build them

✔️ break The UI into a component hierarchy :

  1. react components should follow the "single responsibility principle" (IT SHOULD IDEALLY ONLY DO ONE THING)
  2. each component should match one piece of the data model behind

✔️ build a static version of the app in react

  • implementing the static part requires lots of typing and little thinking (so it should be done first)
  • implementing the interactivity requires lots of thinking and little typing
  • BUILDING A STATIC VERSION ONLY REQUIRES USING PROPS, NOT STATES
  • BUILDING A STATIC VERSION ONLY REQUIRES IMPLEMENTING render() IN COMPONENTS
    • on small projects, build the components hierarchy from the top-down
    • on large projects, build the components hierarchy from the bottom-up and write tests as you go
    • the data model will usually be passed to the top-level component as a prop

✔️ identify the minimal complete representation of the UI state

  • DRY is a requirement here: everything in the app state should be non-redundant
    • data passed via props isn't state
    • data not changing over time isn't state
    • data that can be computed from any component state or props isn’t state
    • remaining data IS state

✔️ identify where your state should live

  • identify the component that should own the state and therefore be able to mutate it
    • identify every component that use state to render
    • identify a common parent component to all the above components
    • either the common parent or another component higher up in the hierarchy should own the state
    • if there's no component where it makes sense to own the state, create a dedicated component just for holding it and place it above the common parent

✔️ add inverse data flow

  • instead of passing data from the top-down, pass functions that update parent component's state as props to the descendant components
  • descendant components will be updated if the parent component is updated.

Notes

✔️ KEYS ONLY MAKE SENSE IN THE CONTEXT OF THE SURROUNDING ARRAY - IT IS THEREFORE ASSESSED THAT THEY NOT NEED TO BE UNIQUE APP-WIDE

✔️ IF A COMPONENT-PRODUCING Array.map() IS TOO NESTED, IT MAY BE A GOOD TIME TO EXTRACT A COMPONENT

✔️ NEVER MAKE THE APP COMPONENT STATEFUL !!! ALL THE SUB-COMPONENTS WILL BE RENDERED WHEN APP STATE CHANGE WHICH CAN YIELD UNDESIRED RESULTS