diff --git a/docs/ui/react/components.md b/docs/ui/react/components.md index bfd6ef7..d3c8aee 100644 --- a/docs/ui/react/components.md +++ b/docs/ui/react/components.md @@ -614,11 +614,720 @@ In this example, the `` establishes some shared implicit state. The `Hello, {props.name}; +} + +const element = ; + +ReactDOM.render( + element, + document.getElementById('root') +); +``` + --- ## Class Components + +The class component, a stateful/container component, is a regular ES6 class that extends the component class of the React library. It is called a stateful component because it controls how the state changes and the implementation of the component logic. Aside from that, they have access to all the different phases of a React lifecycle method. + +**Example:** + +```js +class Welcome extends React.Component { + render() { + return

Hello, {this.props.name}

; + } +} + +const element = ; + +ReactDOM.render( + element, + document.getElementById('root') +); +``` + + + +### Recommended ordering of methods in class component + +* static methods +* constructor() +* getChildContext() +* componentWillMount() +* componentDidMount() +* componentWillReceiveProps() +* shouldComponentUpdate() +* componentWillUpdate() +* componentDidUpdate() +* componentWillUnmount() +* click handlers or event handlers like `onClickSubmit()` or `onChangeDescription()` +* getter methods for render like `getSelectReason()` or `getFooterContent()` +* optional render methods like `renderNavigation()` or `renderProfilePicture()` +* render() + + +### Create a dynamic table + +```js +/** + * Generate dynamic table in React + */ +class Table extends React.Component { + constructor(props) { + super(props) + this.state = { + employees: [ + { id: 10, name: 'Swarna Sachdeva', email: 'swarna@email.com' }, + { id: 20, name: 'Sarvesh Date', email: 'sarvesh@email.com' }, + { id: 30, name: 'Diksha Meka', email: 'diksha@email.com' } + ] + } + } + + renderTableHeader() { + let header = Object.keys(this.state.employees[0]) + return header.map((key, index) => { + return {key.toUpperCase()} + }) + } + + renderTableData() { + return this.state.employees.map((employee, index) => { + const { id, name, email } = employee + return ( + + {id} + {name} + {email} + + ) + }) + } + + render() { + return ( +
+

React Dynamic Table

+ + + {this.renderTableHeader()} + {this.renderTableData()} + +
+
+ ) + } +} +``` + + + +### Prevent component from rendering + +You can prevent component from rendering by returning `null` based on specific condition. This way it can conditionally render component. + +In the example below, the `` is rendered depending on the value of the prop called warn. If the value of the prop is `false`, then the component does not render: + +```js +function WarningBanner(props) { + if (!props.warn) { + return null; + } + + return ( +
+ Warning! +
+ ); +} +``` + +```js +class Page extends React.Component { + constructor(props) { + super(props); + this.state = {showWarning: true}; + this.handleToggleClick = this.handleToggleClick.bind(this); + } + + handleToggleClick() { + this.setState(state => ({ + showWarning: !state.showWarning + })); + } + + render() { + return ( +
+ { /* Prevent component render if value of the prop is false */} + + +
+ ); + } +} + +ReactDOM.render( + , + document.getElementById('root') +); +``` + + +### Set a timer to update every second + +Using `setInterval()` inside React components allows us to execute a function or some code at specific intervals. A function or block of code that is bound to an interval executes until it is stopped. To stop an interval, we can use the `clearInterval()` method. + +**Example:** + +```js +class Clock extends React.Component { + constructor(props) { + super(props) + this.state = { + time: new Date().toLocaleString() + } + } + componentDidMount() { + this.intervalID = setInterval( + () => this.tick(), + 1000 + ) + } + componentWillUnmount() { + clearInterval(this.intervalID) + } + tick() { + this.setState({ + time: new Date().toLocaleString() + }) + } + render() { + return ( +

+ The time is {this.state.time}. +

+ ) + } +} +``` + + + +### Stateful and stateless components + +Stateful and stateless components have many different names. They are also known as: + +– Container vs Presentational components +– Smart vs Dumb components + +The literal difference is that one has state, and the other does not. That means the stateful components are keeping track of changing data, while stateless components print out what is given to them via props, or they always render the same thing. + +**Example:** Stateful/Container/Smart component + +```js +class Welcome extends React.Component { + render() { + return

This is a React Class Component

; + } +} +``` + +**Example:** Stateless/Presentational/Dumb component + +```js +function welcome(props) { + return

This is a React Functional Component

; +} +``` + +|Class Components |Functional Components | +|-----------------------------------------------|-------------------------------------------------------| +|Class components need to extend the component from "React.Component" and create a render function that returns the required element. | Functional components are like normal functions which take "props" as the argument and return the required element.| +|They are also known as stateful components. |They are also known as stateless components.| +|They implement logic and the state of the component.|They accept some kind of data and display it in the UI.| +|Lifecycle methods can be used inside them. |Lifecycle methods cannot be used inside them.| +|It needs to store state therefore constructors are used.|Constructors are not used in it.| +|It has to have a "render()" method inside that. |It does not require a render method.| + + + + +### Purpose of using super constructor with props argument + +The `super()` keyword is used to call the parent constructor. `super(props)` would pass `props` to the parent constructor. + +```js +/** + * super constructor + */ +class App extends React.Component { + constructor(props) { + super(props) + this.state = {} + } + + // React says we have to define render() + render() { + return
Hello world
+ } +} + +export default App +``` + +Here, `super(props)` would call the `React.Component` constructor passing in props as the argument. + + + +### Element, Component and Component instance + +**1. React Elements:** + +A React Element is just a plain old JavaScript Object without own methods. It has essentially four properties: + +* **type**: a String representing an HTML tag or a reference referring to a React Component +* **key**: a String to uniquely identify an React Element +* **ref**: a reference to access either the underlying DOM node or React Component Instance) +* **props**: (properties Object) + +A React Element is not an instance of a React Component. It is just a simplified "description" of how the React Component Instance to be created should look like. + +**2. React Components and React Component Instances:** + +A React Component is used by extending `React.Component`. If a React Component is instantiated it expects a props Object and returns an instance, which is referred to as a React Component Instance. + +A React Component can contain state and has access to the React Lifecycle methods. It must have at least a `render` method, which returns a React Element(-tree) when invoked. + +**Example:** + +```js +/** + * React Component Instances + */ +import React from 'react' +import ReactDOM from 'react-dom' + +class MyComponent extends React.Component { + constructor(props) { + super(props) + console.log('This is a component instance:' + this) + } + + render() { + const another_element =
Hello, World!
+ console.log('This is also an element:' + another_element) + return another_element + } +} + +console.log('This is a component:' + MyComponent) + +const element = +console.log('This is an element:' + element) + +ReactDOM.render(element, document.getElementById('root')); +``` + + + +### What does shouldComponentUpdate do and why is it important + +The `shouldComponentUpdate()` method allows Component to exit the Update life cycle if there is no reason to apply a new render. React does not deeply compare `props` by default. When `props` or `state` is updated React assumes we need to re-render the content. + +The default implementation of this function returns true so to stop the re-render you need to return false here: + +```js +shouldComponentUpdate(nextProps, nextState) { + console.log(nextProps, nextState) + console.log(this.props, this.state) + return false +} +``` + +**Preventing unnecessary renders:** + +The `shouldComponentUpdate()` method is the first real life cycle optimization method that we can leverage in React. It checks the current props and state, compares it to the next props and state and then returns true if they are different, or false if they are the same. This method is not called for the initial render or when `forceUpdate()` is used. + + + +### Purpose of render() function + +The React class components uses render() function. It is used to update the UI. + +**Purpose of render():** + +* React renders HTML to the web page by using a function called render(). +* The purpose of the function is to display the specified HTML code inside the specified HTML element. +* In the render() method, we can read props and state and return our JSX code to the root component of our app. +* In the render() method, we cannot change the state, and we cannot cause side effects ( such as making an HTTP request to the webserver). + +```js +/** + * render() function + * + * React v18.0.0 + */ +import React from "react"; +import { createRoot } from "react-dom/client"; + +class App extends React.Component { + render() { + return

Render() Method Example

; + } +} + +const container = document.getElementById("root"); +const root = createRoot(container); +root.render(); +``` + + +### REACT LIFECYCLE + +#### Different phases of React component lifecycle + +React provides several methods that notify us when certain stage of this process occurs. These methods are called the component lifecycle methods and they are invoked in a predictable order. The lifecycle of the component is divided into four phases. + + + +**1. Mounting:** + +These methods are called in the following order when an instance of a component is being created and inserted into the DOM: + +* `constructor()` +* `getDerivedStateFromProps()` +* `render()` +* `componentDidMount()` + +**2. Updating:** + +The next phase in the lifecycle is when a component is updated. A component is updated whenever there is a change in the component\'s state or props. + +React has five built-in methods that gets called, in this order, when a component is updated: + +* `getDerivedStateFromProps()` +* `shouldComponentUpdate()` +* `render()` +* `getSnapshotBeforeUpdate()` +* `componentDidUpdate()` + +**3. Unmounting:** + +The next phase in the lifecycle is when a component is removed from the DOM, or unmounting as React likes to call it. + +* `componentWillUnmount()` + + + +#### Make component to perform an action only once when the component initially rendered + +**1. Using Class Component:** + +The `componentDidMount()` lifecycle hook can be used with class components. Any actions defined within a `componentDidMount()` lifecycle hook are called only once when the component is first mounted. + +**Example:** + +```js +class Homepage extends React.Component { + componentDidMount() { + trackPageView('Homepage') + } + render() { + return
Homepage
+ } +} +``` + +**2. Using Function Component:** + +The `useEffect()` hook can be used with function components. The `useEffect()` hook is more flexible than the lifecycle methods used for class components. It receives two parameters: + +* The first parameter it takes is a callback function to be executed. +* The optional second parameter it takes is an array containing any variables that are to be tracked. + +The value passed as the second argument controls when the callback is executed: + +* If the second parameter is **undefined**, the callback is executed every time that the component is rendered. +* If the second parameter contains an array of variables, then the callback will be executed as part of the first render cycle and will be executed again each time an item in the array is modified. +* If the second parameter contains an empty array, the callback will be executed only once as part of the first render cycle. + +**Example:** + +```js +const Homepage = () => { + useEffect(() => { + trackPageView('Homepage') + }, []) + + return
Homepage
+} +``` + + + +#### Rendering a list of components from an array of data + +The usual pattern for rendering lists of components often ends with delegating all of the responsibilities of each child component to the entire list container component. But with a few optimizations, we can make a change in a child component not cause the parent component to re-render. + +**Example:** using custom `shouldComponentUpdate()` + +```js +/** + * shouldComponentUpdate() + */ +class AnimalTable extends React.Component { + shouldComponentUpdate(nextProps: Props) { + return !nextProps.animalIds.equals(this.props.animalIds); + } + ... +``` + +Here, `shouldComponentUpdate()` will return false if the props its receiving are equal to the props it already has. And because the AnimalTable is receiving just a List of string IDs, a change in the adoption status won\'t cause AnimalTable to receive a different set of IDs. + + + +#### useEffect() vs componentDidMount() + +In react when we use class based components we get access to lifecycle methods ( like `componentDidMount()`, `componentDidUpdate(), etc ). But when we want use a functional component and also we want to use lifecycle methods, then using useEffect() we can implement those lifecycle methods. + +**1. componentDidMount():** + +The `componentDidMount()` and `useEffect()` run after the mount. However useEffect() runs after the paint has been committed to the screen as opposed to before. This means we would get a flicker if needed to read from the DOM, then synchronously set state to make new UI. + +The `useLayoutEffect()` was designed to have the same timing as componentDidMount(). So `useLayoutEffect(fn, [])` is a much closer match to componentDidMount() than useEffect(fn, []) -- at least from a timing standpoint. + +```js +/** + * componentDidMount() in Class Component + */ +import React, { Component } from "react"; + +export default class SampleComponent extends Component { + componentDidMount() { + // code to run on component mount + } + render() { + return <>componentDidMount Example; + } +} +``` + +**2. useEffect():** + +```js +/** + * useEffect() in Functional Component + */ +import React, { useEffect } from "react"; + +const SampleComponent = () => { + useEffect(() => { + // code to run on component mount + }, []); + return <>useEffect Example; +}; +export default SampleComponent; +``` + +When `useEffect()` is used to get data from server. + +* The first argument is a callback that will be fired after browser layout and paint. Therefore it does not block the painting process of the browser. +* The second argument is an array of values (usually props). +* If any of the value in the array changes, the callback will be fired after every render. +* When it is not present, the callback will always be fired after every render. +* When it is an empty list, the callback will only be fired once, similar to componentDidMount. + + + +#### component constructor called only once? + +React\'s **reconciliation algorithm** assumes that without any information to the contrary, if a custom component appears in the same place on subsequent renders, it\'s the same component as before, so reuses the previous instance rather than creating a new one. + +If you give each component a unique key `prop`, React can use the key change to infer that the component has actually been substituted and will create a new one from scratch, giving it the full component lifecycle. + +```js +renderContent() { + if (this.state.activeItem === 'item-one') { + return ( + + ) + } else { + return ( + + ) + } +} +``` + + +#### componentDidMount() and componentWillMount() + +**componentDidMount():** + +The `componentDidMount()` is executed after the first render only on the client side. This is where AJAX requests and DOM or state updates should occur. This method is also used for integration with other JavaScript frameworks and any functions with delayed execution such as `setTimeout()` or `setInterval()`. + +**Example:** + +```js +import React, { Component } from 'react' + +class App extends Component { + + constructor(props) { + super(props) + this.state = { + data: 'Alex Belfort' + } + } + + getData(){ + setTimeout(() => { + console.log('Our data is fetched') + this.setState({ + data: 'Hello Alex' + }) + }, 1000) + } + + componentDidMount() { + this.getData() + } + + render() { + return ( +
+ {this.state.data} +
+ ) + } +} + +export default App +``` + +**componentWillMount():** + +The `componentWillMount()` method is executed before rendering, on both the server and the client side. `componentWillMount()` method is the least used lifecycle method and called before any HTML element is rendered. It is useful when we want to do something programatically right before the component mounts. + +**Example:** + +```js +import React, { Component } from 'react' + +class App extends Component { + + constructor(props) { + super(props) + this.state = { + data: 'Alex Belfort' + } + } + componentWillMount() { + console.log('First this called') + } + + getData() { + setTimeout(() => { + console.log('Our data is fetched') + this.setState({ + data: 'Hello Alex' + }) + }, 1000) + } + + componentDidMount() { + this.getData() + } + + render() { + return ( +
+ {this.state.data} +
+ ) + } +} + +export default App +``` + + + +#### Good to use setState() in componentWillMount() method? + +Avoid async initialization in `componentWillMount()`. + +`componentWillMount()` is invoked immediately before mounting occurs. It is called before `render()`, therefore setting state in this method will not trigger a re-render. Avoid introducing any side-effects or subscriptions in this method. + +Make async calls for component initialization in `componentDidMount()` instead of `componentWillMount()` + +```js +function componentDidMount() { + axios.get(`api/messages`) + .then((result) => { + const messages = result.data + console.log("COMPONENT WILL Mount messages : ", messages); + this.setState({ + messages: [...messages.content] + }) + }) +} +``` + + + +#### componentWillUnmount() with Functional Components + +The **useEffect()** can be used to manage API calls, as well as implementing **componentWillMount()**, and **componentWillUnmount()**. + +If we pass an empty array as the second argument, it tells useEffect to fire on component load. This is the only time it will fire. + +```js +import React, { useEffect } from 'react'; + +const ComponentExample => () => { + useEffect( () => { + // Anything in here is fired on component mount. + }, []); +} +``` + +If you add a return function inside the useEffect() function, it is triggered when a component unmounts from the DOM. + +```js +import React, { useEffect } from 'react'; + +const ComponentExample => () => { + useEffect(() => { + return () => { + // Anything in here is fired on component unmount. + } + }, []) +} +``` + --- ## Pure Components