React basics
React.js fundamentals
- 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
- 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
- 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 ...
- 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 withcomponent.setState()
- 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)
- 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)
- any method of
- 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 (usedefaultValue
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
- 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
- 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
✔️ react makes you think about apps as you build them
✔️ break The UI into a component hierarchy :
- react components should follow the "single responsibility principle" (IT SHOULD IDEALLY ONLY DO ONE THING)
- 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.
✔️ 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