Prepare better with the best interview questions and answers, and walk away with top interview tips. These interview questions and answers will boost your core interview skills and help you perform better. Be smarter with every interview.
React is a JavaScript library and not a framework. Its primary purpose is to enable developers to create super-efficient and composable UIs. It does not require you to completely re-engineer your application, to begin with, and is easy to adopt. Developers can gradually bring React into the application and can begin by replacing specific UIs/sections in the application.
In line with the tenets of a library, React can be simply imported in, using a standard script tag to get started with. From thereon, developers can gradually envisage a more integrated purpose and schema for implementing React in an existing project. For new projects, React allows you to choose whatever technology stack you want, freeing you from any sort of framework forced architectures & workflows.
A component is an encapsulated element that is made up of UI elements, assembled together to form a functional UI element that may or may not contain it’s own state and logic. Components are building blocks in a React application. As an example, a dialog box component may be used in an application to display warnings and messages. Internally, the component may be made up of divs, headline tags, paragraph tags, buttons etc. Once turned into a component, the dialog box component can be used anywhere in the application to display messages. Every React component must at least implement a render() function that describes the visual elements to display.
The keyword here is reusability. A component composes together elements to offer functionality. Multiple components can then be composed together to form an application.
React makes it easy for developers to create interactive UI components that reactively respond to data, known as the state. By changing state, the UI updates to reflect the change. This behaviour and its internals, though complex in implementation, is hidden from the developer. The developer only needs to describe/declare the UI’s visual elements and the data that drives it. Thereafter, updates are performed automatically. When state changes and the UI has to update, you don’t have to figure out what and how they will update, because React handles that for you. This is why React components are defined as ‘declarative’.
Consider the example of an electrical switch. In its most fundamental form, a switch can have two states, the ‘on’ state and an ‘off’ state. Similarly, React components can optionally maintain a set of internal data properties. When these properties change, the component’s render elements, described in the render() function will be re-rendered, thus displaying the change.
Here’s a React component demonstrating the use of state.
class TheSwitch extends React.Component { constructor() { super(); this.state = { switch: false } this.onClickHandler = this.onClickHandler.bind(this); } onClickHandler() { this.setState({ switch: !this.state.switch }); } render() { const switchText = this.state.switch ? 'Switch is now On' : 'Switch is now Off'; return ( <div> <button onClick={this.onClickHandler}>{switchText}</button> </div> ); } }
In the component above, we have a button designed to act as a toggle switch. In the constructor, we’re declaring a state variable called switch which is initially set to false. In the render function, as long as the switch variable is false, our button’s visible label will be ‘Switch is now Off’. When we click the button, it triggers a function called onClickHandler which then toggles the switch variable’s value from true to false and vice versa. As this value changes, the render function recomputes and the label on the button is updated. All we did was change the value of the state variable, the rest was managed by React.
A React component must include or return a render function. The render function contains the markup which the component is intended to display and this markup can be declared using JSX, which is an XML like syntax that looks quite like HTML with a few changes to the implementation. JSX is not necessary, however, for most developers and use cases is the preferred format for declaring the template markup in the render function. JSX allows unhindered use of JavaScript within the markup itself.
Internally, however, JSX is simply syntactic sugar for React.createElement() function that actually creates individual element nodes in the Virtual DOM.
For instance,
JSX <div><h1>Hello there!</h1></div Converted JS Function React.createElement( "div", null, React.createElement( "h1", null, "Hello there!" ) );
A function component is a simple JavaScript function that returns a React element, written in JSX or using JavaScript directly. This function can accept a props argument to enable the use of properties for letting data into the component. This data can then be rendered within the component’s markup. A function component is a bare-bones component and does not include local state, lifecycle methods, event handler functions etc. The only exception to this rule is when you use hooks to plug in state and other features.
A function component looks like this:
function TitleBar(props) { return ( <h1>{props.message}</h1> ); }
In another component, this can then be used like so:
<TitleBar message=”Hey there!” />
For a lot of situations including those involving composability, and where a state is not required, functional components are the lightest and easiest to envision and implement solutions.
Props or properties enable us to pass data into a component as an inlet. Props should never be modified by the component. Props are an essential feature that enables components to be composable and reusable.
A prop can receive static as well as dynamically computed data. Using a prop is similar to setting an attribute on an HTML element.
For example:
<Title message=”Hey there!” />
In the above example, we’re passing a static string ‘Hey there!’ as the value of a prop called ‘message’. This can then be used as needed within the component.
A component that implements local state is a stateful component. Such components, unlike function components, are created as an ES6 class by extending the React.Component class. The class can then implement its own local state and can also implement methods to manipulate this state by way of user interaction for instance. Here’s a simple example of a stateful component.
class TheSwitch extends React.Component { constructor() { super(); this.state = { switch: false } this.onClickHandler = this.onClickHandler.bind(this); } onClickHandler() { this.setState({ switch: !this.state.switch }); } render() { const switchText = this.state.switch ? 'Switch is now On' : 'Switch is now Off'; return ( <div> <button onClick={this.onClickHandler}>{switchText}</button> </div> ); } }
This component implements local state which is being modified using an event handler. When the local state changes, the render function re-renders, reflecting the change in the state.
A list of items can be rendered using a .map method to iterate over a given Array, available as either a state variable or a prop. Here is an example of an unordered list of fruits being rendered using an Array, set as a state variable. By using the .map method, we first concatenate together instances of the list item with the value from the Array and then render that unit as a whole within a pair of UL tags.
class FruitBasket extends React.Component { constructor() { super(); this.state = { fruits: ['Apples', 'Oranges', 'Kiwi', 'Strawberries'] } } render() { const basket = this.state.fruits.map(fruit => (<li>{fruit}</li>)); return (<ul>{basket}</ul>); } } ReactDOM.render( <FruitBasket />, document.getElementById('app') );
The click event can be captured by using the onClick property to define an event handler function which then gets an instance of a SyntheticEvent. The SyntheticEvent enables universal cross-browser access to native events. In the statement below, we’re assigning a click event handler to a button. When this button is clicked, the clickEventHandler method will be invoked.
<button onClick={clickEventHandler}>Signup</button>
Event handlers in React receive an instance of a SyntheticEvent which is a wrapper that provides seamless and cross browser access to native events. This ensures that event handling works across all browsers, even if browser specific APIs differ.
The dangerouslySetInnerHTML property can be used to render raw HTML in a component. This is only for fringe cases and is not recommended at all because it can expose the application to potential XSS attacks, where attackers can inject and execute malicious code in a React application.
The Document Object Model or DOM defines an interface that allows languages such as JavaScript to access and manipulate an HTML document. Elements are represented by nodes in a tree and the interface allows us to manipulate them. This interface, however, comes with a cost and a large number of very frequent DOM operations will slow down the page.
React solves this problem by creating an in-memory representation of the actual DOM. When manipulation is needed, instead of performing it on the real DOM, the calculations and operations are performed in memory on the Virtual DOM. This is naturally faster and allows the Virtual DOM algorithm to compute the most optimised way to update the actual DOM structure.
The culmination of this computation is then performed on the actual DOM. The process of keeping the actual DOM in sync with the virtual DOM is known as reconciliation. The reconciliation engine in React (ver 16 onwards) is known as Fiber. The Virtual DOM is at the heart of React and is what makes React super fast and performant.
The way Virtual DOM works is quite simple to understand. First, an in-memory representation of the DOM is created. Then, whenever state data, or props result in an update to a component, it is first implemented in the Virtual DOM. The updated Virtual DOM is then compared with the actual DOM in a ‘diffing’ stage. The difference between the two is then applied to the actual DOM in a single step.
The key property allows React to identify and keep track of list items that it renders. This is critically important if the list is sorted or updated at a later time. Instead of mutating and re-rendering the entire list again on an update, the unique keys allow React to identify the nodes where the actual changes are needed. Only those nodes are then updated to reflect the change. This naturally makes the process much more efficient. For instance, let’s examine the following piece of code:
import React from "react"; import ReactDOM from "react-dom"; function shuffle(array) { // a function that shuffles the contents of the array } function FruitBox(props) { return ( <div className=”fruitBox”> <p>{props.fruit}</p> <input type="text" /> </div> ); } class FruitBasket extends React.Component { constructor() { super(); this.state = { fruits: ["Apples", "Oranges", "Kiwi", "Strawberries"] }; this.clickShuffle = this.clickShuffle.bind(this); } clickShuffle(e) { e.preventDefault(); this.setState({ fruits: shuffle(this.state.fruits) }) } render() { console.log(this.state.fruits); const basket = this.state.fruits.map((fruit, index) => ( <FruitBox fruit={fruit} key={fruit} /> )); return ( <div> {basket} <button onClick={this.clickShuffle}>Shuffle Basket</button> </div> ); } } const rootElement = document.getElementById("root"); ReactDOM.render(<FruitBasket />, rootElement);
The above example renders four instances of <FruitBox> which displays a paragraph tag with the name of fruit from the state variable ‘fruits’ and it also displays an input box.
When the key property is NOT set : Type something in any input box and hit the shuffle basket button. You’ll notice the names of the fruits switch places in the list because the array shuffles. But the input box where you typed stays in the same place. This happens because React is unable to keep track of individual nodes. When the array updates, the updates are applied selectively to the paragraph node elements where the update is supposed to show up.
But we would ideally want the entire <FruitBox> instance including the edited input box to move along to a new position as the array shuffles. This can be done by including a unique value to the key property. In the code above, we’ve used the name of the fruit as the key value. You should not use the Array’s index value as it recomputes when the Array updates and it naturally does not reflect a 1-1 relationship with the individual element’s contents.
React apps use the Virtual DOM and as a result, direct access to a component’s DOM element is not advised. But for special cases, if you do need access, then the ‘ref’ property may be used. Here’s an example:
class MyButton extends React.Component { constructor() { super(); this.buttonRef = React.createRef(); } componentDidMount() { this.buttonRef.current.innerText = "Register Now!"; } render() { return (<button ref={this.buttonRef}>Submit</button>); } }
In the example above, we’re first creating a new reference and then we’re using it in the componentDidMount method to change the button’s text. Refs may be used in special cases such as controlling media playback, setting focus and integration with third party DOM manipulation libraries. For everything else, even if it tends to solve a problem, one must steer clear of refs in favour of React specific techniques such as using state to cause updates/change to elements.
The value of an input box can be accessed in an onChange event handler using event.target.value. Here’s an example:
class MyInputBox extends React.Component { constructor() { super(); this.state = { message: ‘’ } this.onChangeHandler = this.onChangeHandler.bind(this); } onChangeHandler(event) { this.setState({ message: event.target.value }); } render() { return (<div> <p>{this.state.message}</p> <input type="text" onChange={this.onChangeHandler} /> </div>); } }
In the above example, whenever the user types in the box, the onChangeHandler function receives as synthetic event which can be used to access the current value of the input box. Here it is being used to update a state variable ‘message’, which is being rendered in a paragraph tag. As the user types, the text updates.
Major lifecycle methods include :
To intercept errors in a child component such as during rendering, we can implement the static getDerivedStateFromError(error) method in the parent. The purpose of this method is to update/restore the state so that a fallback interface or component may be displayed. This method should return an object that contains one or more state variables with their value set. There is no need to run the setState function here.
Though not originally so, React has evolved into a renderer agnostic library. This means that the React core is not concerned with the target medium as far as the final rendered outcome is concerned. This has allowed the creation of React Native where there is no DOM and yet you can use React to render native components on Android and iOS.
Having said that, the ReactDOM package offers DOM specific methods to React. You would use this when working on web applications that need to render in a browser. One of the most commonly used functions from this library is the ReactDOM.render() which is used to render the top most component in the DOM. Components themselves should not have the need to use functions from ReactDOM directly, however some, like the ReactDOM.findDOMNode() may be used in fringe cases.
Another function from this package is hydrate() which is used to refill a DOM container with pre-rendered HTML from a React application. This is achieved by using the ReactDOMServer package to pre-render an application before injecting it into the DOM.
HTML elements such as input fields, textarea and other form elements maintain their own internal state in the DOM. This lets the users type and set a value for instance. In a React application, the concept of state is central to a component and not to individual elements. In a React app, the state should be the central source of truth for everything. This also means that interactive components and elements must derive their contents from the state and also update that state when the user updates the field’s values. This ensures a synchronised state and prevents situations where the input element’s contents may differ from its state variable in the component. This way, React and its state have full control over the element/component. Such components are known as controlled components because their value is under full control of the React component’s state.
To implement this, a component simply derives its value from a state variable and when the user changes the component’s value, it is written back to the state. This creates a two way binding between the component and a state variable.
The following example illustrates a controlled component.
class MyInputBox extends React.Component { constructor() { super(); this.state = { message: '' } this.onChangeHandler = this.onChangeHandler.bind(this); } onChangeHandler(event) { this.setState({ message: event.target.value }); } render() { return (<div> <input type="text" value={this.state.message} onChange={this.onChangeHandler} /> </div>); } }
In the example above, the input field in the render function is a controlled component because its value is set to a state variable named message and the onChange event handler is designed to update this state variable, thereby creating a 2 way binding. The state variable ‘message’ is thus the single source of truth and controls the component.
React uses the value of the key property as a way of identifying the component instance in the Virtual DOM. This is critically important if the list has to update over time. The most important thing about the ‘key’ value is that it must be unique and not something that mutates over time. Otherwise the relationship between the contents of the component and the key value cannot be established. The index value of an Array recomputes and changes as the Array mutates over time and hence the index value can never reflect a true relationship with the list item’s contents.
Therefore, the index value should never be used as a ‘key’.
Initial state refers to pre-configured value(s) of state variables when the component renders for the first time in the app. This can be easily done using props. Here’s an example:
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { message: props.message } this.onClickHandler = this.onClickHandler.bind(this); } onClickHandler() { this.setState({ message: this.state.message.split('').reverse().join('') }); } render() { return (<div> <p>{this.state.message}</p> <button onClick={this.onClickHandler}>Reverse Message</button> </div>); } }
You can use this component like so:
<MyComponent message=”This is a message” />
The prop ‘message’ here sets an initial value of the message state variable. Once the component renders, the ‘Reverse Message’ button can be pressed to reverse the value of the message variable in the state.
Conditional rendering can be implemented easily by using simple conditional statements like the ‘if’ statement. Here’s an example:
function LogOut() { return (<p>Logout now</p>); } function LogIn() { return (<p>Login now</p>); } function UserControl(props) { if(props.isLoggedIn) { return (<LogOut />); } return (<LogIn />); } ReactDOM.render(<UserControl isLoggedIn={false} />, document.getElementById("app"));
In the example above, the <UserControl /> component features a prop called isLoggedIn which can be set to true or false. Internally, it conditionally renders either <LogIn /> component or <LogOut /> component based on the value of the isLoggedIn prop.
The create-react-app is a command line utility that can be installed to easily bootstrap a React application. It implements webpack to setup a build system which not only makes it easy to develop React apps but also offers features like hot reloading, asset and spreadsheet management, the ability to implement code splitting and several other features that are recommended by the React team. When the app has been built, the developer can generate a final production build which is highly optimised and ready for deployment on the server. The create-react-app is the officially recommended way for building React apps.
Traditionally and in the past, when using nested components, the only way to pass down configuration and data to child components has been props. So, the parent component would set a prop on child component and the child would set a prop on its child to pass in data. This becomes a problem especially when a piece of configuration is required globally across multiple components in the application tree/structure. In this case, mapping a top to down pipeline using props can be quite a challenge. Imagine the name of a user who’s logged in. Once logged in, this name may be needed in many components.
One of the newer ways to solve this problem is to use context where you set a context variable with the value that you wish to use on descendent components. A context provider then wraps the child component hierarchy and any component in that tree can then become a consumer and fetch the value from the context.
In this first code example, we’re using props to pass down a state variable called user which is ultimately consumed by a component called <UserBox />. In doing so we have to pass through <Dashboard /> because that is how we would be able to pass the value down the tree.
Using Props class MyApp extends React.Component { constructor() { super(); this.state = { user: "John Doe" }; } render() { return ( <div> <Dashboard user={this.state.user} /> </div> ); } } function Dashboard(props) { return ( <div className="dashboard"> <h1>Dashboard</h1> <UserBox user={props.user} /> </div> ); } function UserBox(props) { return <h3>{props.user}</h3>; }
Instead of charting a downstream channel through props, we can instead use Context as shown in the following code segment:
const UserName = React.createContext(""); class MyApp extends React.Component { constructor() { super(); this.state = { user: "John Doe" }; } render() { return ( <div> <UserName.Provider value={this.state.user}> <Dashboard /> </UserName.Provider> </div> ); } } function Dashboard() { return ( <div className="dashboard"> <h1>Dashboard</h1> <UserName.Consumer> {value => <UserBox user={value} />} </UserName.Consumer> </div> ); } function UserBox(props) { return <h3>{props.user}</h3>; }
Here, we have a Provider on the parent. The child component is then wrapped in a context consumer which returns a component. The consumer subscribes to the provider and all consumers get an updated value when the provider updates it.
The value of a state variable is used across many components in a large application. That is why, in the hierarchy of components, the state should be positioned at the closest parent of components so that no additional mechanism is required to synchronise data across components. The most fundamental data flow strategy in React is the top-down data flow, where data flows from the top most component, down through the props/context to descendent child components. That is why it makes logical and practical sense to hoist the state at the parent or the nearest possible parent.
In a component’s render function, you cannot have multiple top level components as adjacent nodes. You would need to wrap them inside a div or some other form of top level component to make it work. This often introduces an extra DOM node in the form of a wrapper div or some other element, which is not required by the layout but is merely introduced as a necessity. In many instances, it can also lead to invalid HTML. Such as introducing a div inside an UL node to wrap a set of LI nodes. Fragments help you resolve this problem. You can enclose your top level sibling nodes inside the fragments component (as shown below) and an unnecessary DOM node is not introduced.
THE PROBLEM
function MyApp() { return ( <ul> <Fruits /> </ul> ); } function Fruits() { return ( <li>Apples</li> <li>Strawberries</li> ); }
This code is invalid because adjacent and top level <li> nodes in the Fruits component are not permitted in JSX. To solve this, you’d be tempted to wrap them in a div but that would render invalid HTML.
React allows you to solve this problem using Fragments as shown below:
function MyApp() { return ( <ul> <Fruits /> </ul> ); } function Fruits() { return ( <> <li>Apples</li> <li>Strawberries</li> </> ); }
By wrapping the adjacent LI nodes in a pair of fragment components <></>, an unwanted DOM node won’t be inserted and you would be able to render multiple adjacent components/elements easily.
A generally ideal place to fetch data from an API call is the componentDidMount() lifecycle method. This method is executed when the component has been inserted into the DOM tree and thus has been mounted. At this time, a network call to fetch data can be done and upon successful completion, the data may be used to update the state thereby causing the component to re-render and display the fetched data.
To fetch the data, a commonly used library is axios. However, something as fundamental as the fetch API may be used. An example using Axios is shown below.
class Users extends React.Component { constructor() { super(); this.state = { users: [] }; } componentDidMount() { axios.get("https://jsonplaceholder.typicode.com/users").then(data => this.setState({ users: data.data }) ); } render() { return ( <div> {this.state.users.map(user => ( <UserBox data={user} key={user.id} /> ))} </div> ); } } function UserBox(props) { return ( <div> <h2>{props.data.name}</h2> <h3> {props.data.email} | {props.data.phone} </h3> <hr /> </div> ); }
The example above brings in data from an API and displays that in the app.
Lazy loading refers to deferred loading of components to a time when they’re actually needed in the application. Generally, when we use webpack as the build tool for bundling a React app, such as when using the create-react-app tool, the build process would end up bundling all components into one big bundle file which loads in full before the app can execute. While this is usually fine, it isn’t a good practice when your app is large or has grown large over time, as you’ve added new features and capabilities.
In such a case, an ideal situation would be to split the code across multiple files/components and load them only when they’re needed in the application. This is what is called ‘lazy-loading’ a component.
React’s Suspense API provides us with such a toolkit that makes it a breeze to lazy load components. Here’s an example:
const DataSheet = React.lazy(() => import('./DataSheet')); function Loading() { return (<div className="loading">Loading Data...</div>); } class MainComponent extends React.Component { constructor() { super(); } render() { return ( <Suspense fallback={<Loading />}> <DataSheet /> </Suspense> ); } }
In the example above, the <DataSheet /> component is being loaded from an external file using an import statement. When you build this project, webpack sees the import statement and splits the bundle so that <DataSheet> resides in a separate file and not bundled with main application.
When the <MainComponent> is rendered, the <DataSheet> component will be dynamically loaded and while it is being loaded, a fallback <Loading> component will be displayed. The lazy loading is made possible using React.lazy() which is part of the Suspense API.
Strict Mode is the name of a special tool that developers can use to highlight a set of potential problems with React components. This only works in development and raises warnings about unsafe lifecycle methods which are not recommended, legacy String ref api and various other deprecated and legacy API usage. More and more warnings will be added as React continues to evolve. Using this tool involves wrapping one or more components in the <React.StrictMode></React.StrictMode> component as shown below:
class MainComponent extends React.Component { constructor() { super(); } render() { return ( <React.StrictMode> <DataSheet /> </React.StrictMode> ); } }
The <React.StrictMode> component doesn’t add any node to the DOM and is merely used to wrap components for whom you need warnings to be issued.
It is a common practice to bind class methods such as event handler functions in the constructor before using it in the render function. The example below demonstrates this practice:
class MyApp extends React.Component { constructor() { super(); this.clickHandler = this.clickHandler.bind(this); } clickHandler() { // Do something here when the button is clicked } render() { return ( <button onClick={this.clickHandler}>Do Something!</button> ); } }
In JavaScript, if you create a reference to a class method in another method, the ‘this’ context is not bound to the original function. Consider the following example of a standard JavaScript class (Not necessarily a React method):
class doSomething { constructor() { } create() { this.message = "Hey there!"; } render() { const fnRef = this.create; fnRef(); console.log(this.message); } } const doSomethingInstance = new doSomething(); doSomethingInstance.render(); // undefined
In the above example, a reference to the ‘create’ class method is created in the render method first. This breaks the original ‘this’ context of the create() method which was bound to the doSomething class instance and creates a local ‘this’ context. So, ‘this.message’ does NOT set an instance variable, and instead sets a context variable, local to the create() method. As a result, the render function is unable to access this context and hence outputs undefined. This is because in JavaScript, class methods are not bound by default.
If we first bind the function in the constructor by calling this.create = this.create.bind(this) the ‘this’ context is bound correctly and preserved and hence ‘this.message’ refers to the ‘this’ context of the class instance.
If you do not wish to bind the methods first, then you can also use arrow functions as shown below:
class MyApp extends React.Component { constructor() { super(); } clickHandler() { // Do something here when the button is clicked } render() { return ( <button onClick={e => this.clickHandler(e)}>Do Something!</button> ); } }
In some cases, it may be required to force a re-render on a component even when the state/props have not updated/changed. This can be achieved by using this.forceUpdate() in the component. The following example creates a simple clock that uses forceUpdate to re-render every 1 second.
class MyClock extends React.Component { constructor() { super(); } componentDidMount() { this.intervalTimer = setInterval(() => this.forceUpdate(), 1000); } componentWillUnmount() { clearInterval(this.intervalTimer); } render() { const time = new Date().toLocaleTimeString(); return <h1>{time}</h1>; } }
The above example is not necessarily the most efficient one, but is an example of how forceUpdate() may be used. While forceUpdate() works, it is not recommended and should only be used when you are unable to update state/props due to some logical reason.
React components are subclasses of the React.Component parent class. If you have a constructor() in the component class, then you need to call super() to enable the class to access methods from the parent class as well as for initializing the ‘this’ context variable. While a constructor is NOT necessary for class based components, you do need to initialize one if your component will use props or state. When using props, it is mandatory to initialize the constructor with super(props) or else, neither the ‘this’ nor ‘this.props’ would be available inside class methods. The invocation of super() in the constructor of subclasses is required as per ES6.
Function components are essentially functions that return a React component, written typically using JSX. Function components can also accept props but they do not offer lifecycle methods and state management/manipulation. To solve this problem, React has introduced an experimental API called ‘hooks’ which lets your function components ‘hook’ into React features. To implement state in a function component, we can use the ‘useState’ hook as shown in the example below:
import { useState } from "react"; function MyApp() { const [greeting, reverseGreeting] = useState("Hello there!"); return ( <div> <h1>{greeting}</h1> <button onClick={() => reverseGreeting( greeting .split("") .reverse() .join("") ) } > Change Text </button> </div> ); }
In the example above, we’re using the experimental useState hook from React 16.7.0-alpha.2. The useState() allows us to initialize a state variable ‘greeting’ and a function ‘reverseGreeting’ that will modify the state. We can call the reverseGreeting function to update the state in the component.
When using props, we are dependent on the consumer to ensure correct data types. Having said that, we do have a way to validate props and raise warnings if the incoming data does not adhere to the correct data type. This, however only throws warnings in the development phase and has no effect in a production build.
import PropTypes from "prop-types"; function MyApp(props) { return ( <div> <h1>{props.name}</h1> <h4>{props.age} years old</h4> </div> ); } MyApp.propTypes = { name: PropTypes.string, age: PropTypes.number };
We begin by importing PropTypes from the prop-types package which brings in a number of prop validators. After defining your component, we can then configure the propTypes object to declare every prop and consequent validators as shown above.
This will raise warnings on the console if the incoming data type does not match the validator type. Custom validators can also be created.
Refs are used to access the underlying DOM node of a component/element. While refs should ideally be avoided, there are times where you might need to access the DOM node directly. When authoring components/libraries for public distribution and use, it often becomes necessary to offer access to a component’s internal DOM nodes using ‘refs’. Forwarding refs allow you to offer a ref to an internal child component/node.
Here’s an example:
class Dialog extends React.Component { constructor(props) { super(props); this.state = { showDialog: true }; } closeDialog() { this.setState({ showDialog: false }); } render() { const { forwardedRef, ...rest } = this.props; if (this.state.showDialog) { return ( <div className="dialog"> <a className="close-btn" ref={forwardedRef} onClick={() => this.closeDialog()} > Close Dialog </a> {this.props.children} </div> ); } else { return null; } } } const DialogComponent = React.forwardRef((props, ref) => ( <Dialog {...props} forwardedRef={ref} /> )); class MyApp extends React.Component { constructor() { super(); this.buttonRef = React.createRef(); } componentDidMount() { this.buttonRef.current.innerText = "Close this Dialog"; } render() { return ( <DialogComponent ref={this.buttonRef}> <h1>This is a dialog box!</h1> </DialogComponent> ); } }
In the example above, the Dialog component includes a button (a hyperlink) that is being turned into a ref which we’re forwarding from <DialogComponent> using React.forwardRef(). We can then access and modify the hyperlink node from whichever component that uses the <DialogComponent>.
Here we’re changing the innerText property of the hyperlink from the <MyApp /> component.
React offers three ways to ensure a component only re-renders when its props update and not otherwise.
The first of these methods involves the use of the shouldComponentUpdate() method that is available in class based components. By using this method, we can control the re-renders for a given component based on whether state variables or props have indeed updated or not. The example below will only permit re-renders when the message prop updates. While this works, the use of the shouldComponentUpdate() method incurs a performance cost and must be avoided wherever possible.
class Dialog extends React.Component { constructor(props) { super(props); } shouldComponentUpdate(nextProps, nextState) { return this.props.message !== nextProps.message; } render() { return <div className="dialog-box">{this.props.message}</div>; } }
The second method, also available for class based components is the use of React.PureComponent. A Pure Component does not implement a configurable shouldComponentUpdate() method but implements a built-in shallow comparison of the component’s state and props so components will only re-render if the props or state actually changes. The comparison however is shallow and complex objects will not deliver the desired behaviour. The following example implements a pure component.
class Dialog extends React.PureComponent { constructor(props) { super(props); } render() { return <div className="dialog-box">{this.props.message}</div>; } }
The third method can be used with function components which do not implement an internal shouldComponentUpdate method. In this method, we use the React.memo() higher order component to implement basic memoization. This is, just like the PureComponent, only does a shallow comparison and will not deliver desired results on complex prop/state objects.
function Dialog(props) { return <div className="dialog-box">{props.message}</div>; } const DialogComponent = React.memo(Dialog);
If the default memoization behaviour is not preferred, then it can be customised like the shouldComponentUpdate() method as shown below:
function Dialog(props) { return <div className="dialog-box">{props.message}</div>; } const DialogComponent = React.memo( Dialog, (prevProps, nextProps) => prevProps.message === nextProps.message );
It is indeed possible to selectively render as component in a location other than the nearest parent’s DOM node by using Portals. A portal, as the name implies is a way to direct the rendering of a component to a DOM node of choice irrespective of the parent component’s DOM target. Here’s an example:
class MyApp extends React.PureComponent { constructor() { super(); this.state = { count: 10, timeout: false }; } componentDidMount() { setTimeout(() => { this.setState({ timeout: true }); }, this.state.count * 1000); } render() { if (this.state.timeout) { return ReactDOM.createPortal( <Dialog message="Timeout!!" />, document.getElementById("dialog") ); } else { return <h1>This message will self destruct!</h1>; } } }
In the example above, we implement as simple countdown that counts down from 10 seconds. When it reaches 0, an instance of the Dialog component is rendered into a div with the id ‘dialog’ which is different from the node where the MyApp component renders. We’ve used the createPortal() method from the ReactDOM package to achieve this functionality.
A higher order component is a function that accepts a component and returns a valid component after augmenting it with custom logic. This is not something that is React specific and is merely a pattern which let’s developers write logic that can be shared across multiple components. The react-router package as an example offers higher order components. Here’s an example of a higher order component:
function Dialog(props) { return <div className="dialog-box">{props.message}</div>; } function Message(props) { return <h1>{props.message}</h1>; } function withCountdown(WrappedComponent, countdownSec = 10) { class SelfDestruct extends React.Component { constructor(props) { super(props); this.state = { timeout: false }; } componentDidMount() { setTimeout(() => { this.setState({ timeout: true }); }, countdownSec * 1000); } render() { if (this.state.timeout) { return ReactDOM.createPortal( <Dialog message="Timeout!!" />, document.getElementById("dialog") ); } else { return <WrappedComponent {...this.props} />; } } } return SelfDestruct; } const MyApp = withCountdown(Message, 5); const rootElement = document.getElementById("root"); ReactDOM.render( <MyApp message="This message will self destruct soon!" />, rootElement );
In the example above, the withCountdown() higher order function takes in a component and a value in seconds. The component is then shown for the set duration in seconds, after which a timeout message is displayed. The countdown logic is wrapped up inside the withCountdown() HOC and the consumer need not be concerned about the internals. This makes HOCs a very powerful design pattern for writing shareable and reusable logic in React apps.
The code above will output 40 as the value of the state variable ‘count’. This is because, this.setState() is asynchronous and it is accessing this.state.count which will always reflect the ‘rendered’ value of the state variable ‘count’. When the component renders, it reads the original value of this.state. This is why, the second setState() invocation above also ends up reading the value of this.state.count as 10, which is multiplied by 4 to result in 40.
If you’re seeking 80 as the result, you have to use update functions with setState() as shown below. In this case, all updater functions are batch processed and the state updated, only after the batch has been computed in full. Instead of this.state, we’re accessing state which is the updater function’s internal value and does not depend on rendered state values.
class MathsBrain extends React.Component { constructor() { super(); this.state = { count: 10 }; } componentDidMount() { this.setState(state => { return { count: state.count * 2 }; }); this.setState(state => { return { count: state.count * 4 }; }); } render() { return <div>Count: {this.state.count}</div>; } }
The revised component above, implements updater functions instead of accessing this.state.count which will always reflect the rendered value of the state.
React is a JavaScript library and not a framework. Its primary purpose is to enable developers to create super-efficient and composable UIs. It does not require you to completely re-engineer your application, to begin with, and is easy to adopt. Developers can gradually bring React into the application and can begin by replacing specific UIs/sections in the application.
In line with the tenets of a library, React can be simply imported in, using a standard script tag to get started with. From thereon, developers can gradually envisage a more integrated purpose and schema for implementing React in an existing project. For new projects, React allows you to choose whatever technology stack you want, freeing you from any sort of framework forced architectures & workflows.
A component is an encapsulated element that is made up of UI elements, assembled together to form a functional UI element that may or may not contain it’s own state and logic. Components are building blocks in a React application. As an example, a dialog box component may be used in an application to display warnings and messages. Internally, the component may be made up of divs, headline tags, paragraph tags, buttons etc. Once turned into a component, the dialog box component can be used anywhere in the application to display messages. Every React component must at least implement a render() function that describes the visual elements to display.
The keyword here is reusability. A component composes together elements to offer functionality. Multiple components can then be composed together to form an application.
React makes it easy for developers to create interactive UI components that reactively respond to data, known as the state. By changing state, the UI updates to reflect the change. This behaviour and its internals, though complex in implementation, is hidden from the developer. The developer only needs to describe/declare the UI’s visual elements and the data that drives it. Thereafter, updates are performed automatically. When state changes and the UI has to update, you don’t have to figure out what and how they will update, because React handles that for you. This is why React components are defined as ‘declarative’.
Consider the example of an electrical switch. In its most fundamental form, a switch can have two states, the ‘on’ state and an ‘off’ state. Similarly, React components can optionally maintain a set of internal data properties. When these properties change, the component’s render elements, described in the render() function will be re-rendered, thus displaying the change.
Here’s a React component demonstrating the use of state.
class TheSwitch extends React.Component { constructor() { super(); this.state = { switch: false } this.onClickHandler = this.onClickHandler.bind(this); } onClickHandler() { this.setState({ switch: !this.state.switch }); } render() { const switchText = this.state.switch ? 'Switch is now On' : 'Switch is now Off'; return ( <div> <button onClick={this.onClickHandler}>{switchText}</button> </div> ); } }
In the component above, we have a button designed to act as a toggle switch. In the constructor, we’re declaring a state variable called switch which is initially set to false. In the render function, as long as the switch variable is false, our button’s visible label will be ‘Switch is now Off’. When we click the button, it triggers a function called onClickHandler which then toggles the switch variable’s value from true to false and vice versa. As this value changes, the render function recomputes and the label on the button is updated. All we did was change the value of the state variable, the rest was managed by React.
A React component must include or return a render function. The render function contains the markup which the component is intended to display and this markup can be declared using JSX, which is an XML like syntax that looks quite like HTML with a few changes to the implementation. JSX is not necessary, however, for most developers and use cases is the preferred format for declaring the template markup in the render function. JSX allows unhindered use of JavaScript within the markup itself.
Internally, however, JSX is simply syntactic sugar for React.createElement() function that actually creates individual element nodes in the Virtual DOM.
For instance,
JSX <div><h1>Hello there!</h1></div Converted JS Function React.createElement( "div", null, React.createElement( "h1", null, "Hello there!" ) );
A function component is a simple JavaScript function that returns a React element, written in JSX or using JavaScript directly. This function can accept a props argument to enable the use of properties for letting data into the component. This data can then be rendered within the component’s markup. A function component is a bare-bones component and does not include local state, lifecycle methods, event handler functions etc. The only exception to this rule is when you use hooks to plug in state and other features.
A function component looks like this:
function TitleBar(props) { return ( <h1>{props.message}</h1> ); }
In another component, this can then be used like so:
<TitleBar message=”Hey there!” />
For a lot of situations including those involving composability, and where a state is not required, functional components are the lightest and easiest to envision and implement solutions.
Props or properties enable us to pass data into a component as an inlet. Props should never be modified by the component. Props are an essential feature that enables components to be composable and reusable.
A prop can receive static as well as dynamically computed data. Using a prop is similar to setting an attribute on an HTML element.
For example:
<Title message=”Hey there!” />
In the above example, we’re passing a static string ‘Hey there!’ as the value of a prop called ‘message’. This can then be used as needed within the component.
A component that implements local state is a stateful component. Such components, unlike function components, are created as an ES6 class by extending the React.Component class. The class can then implement its own local state and can also implement methods to manipulate this state by way of user interaction for instance. Here’s a simple example of a stateful component.
class TheSwitch extends React.Component { constructor() { super(); this.state = { switch: false } this.onClickHandler = this.onClickHandler.bind(this); } onClickHandler() { this.setState({ switch: !this.state.switch }); } render() { const switchText = this.state.switch ? 'Switch is now On' : 'Switch is now Off'; return ( <div> <button onClick={this.onClickHandler}>{switchText}</button> </div> ); } }
This component implements local state which is being modified using an event handler. When the local state changes, the render function re-renders, reflecting the change in the state.
A list of items can be rendered using a .map method to iterate over a given Array, available as either a state variable or a prop. Here is an example of an unordered list of fruits being rendered using an Array, set as a state variable. By using the .map method, we first concatenate together instances of the list item with the value from the Array and then render that unit as a whole within a pair of UL tags.
class FruitBasket extends React.Component { constructor() { super(); this.state = { fruits: ['Apples', 'Oranges', 'Kiwi', 'Strawberries'] } } render() { const basket = this.state.fruits.map(fruit => (<li>{fruit}</li>)); return (<ul>{basket}</ul>); } } ReactDOM.render( <FruitBasket />, document.getElementById('app') );
The click event can be captured by using the onClick property to define an event handler function which then gets an instance of a SyntheticEvent. The SyntheticEvent enables universal cross-browser access to native events. In the statement below, we’re assigning a click event handler to a button. When this button is clicked, the clickEventHandler method will be invoked.
<button onClick={clickEventHandler}>Signup</button>
Event handlers in React receive an instance of a SyntheticEvent which is a wrapper that provides seamless and cross browser access to native events. This ensures that event handling works across all browsers, even if browser specific APIs differ.
The dangerouslySetInnerHTML property can be used to render raw HTML in a component. This is only for fringe cases and is not recommended at all because it can expose the application to potential XSS attacks, where attackers can inject and execute malicious code in a React application.
The Document Object Model or DOM defines an interface that allows languages such as JavaScript to access and manipulate an HTML document. Elements are represented by nodes in a tree and the interface allows us to manipulate them. This interface, however, comes with a cost and a large number of very frequent DOM operations will slow down the page.
React solves this problem by creating an in-memory representation of the actual DOM. When manipulation is needed, instead of performing it on the real DOM, the calculations and operations are performed in memory on the Virtual DOM. This is naturally faster and allows the Virtual DOM algorithm to compute the most optimised way to update the actual DOM structure.
The culmination of this computation is then performed on the actual DOM. The process of keeping the actual DOM in sync with the virtual DOM is known as reconciliation. The reconciliation engine in React (ver 16 onwards) is known as Fiber. The Virtual DOM is at the heart of React and is what makes React super fast and performant.
The way Virtual DOM works is quite simple to understand. First, an in-memory representation of the DOM is created. Then, whenever state data, or props result in an update to a component, it is first implemented in the Virtual DOM. The updated Virtual DOM is then compared with the actual DOM in a ‘diffing’ stage. The difference between the two is then applied to the actual DOM in a single step.
The key property allows React to identify and keep track of list items that it renders. This is critically important if the list is sorted or updated at a later time. Instead of mutating and re-rendering the entire list again on an update, the unique keys allow React to identify the nodes where the actual changes are needed. Only those nodes are then updated to reflect the change. This naturally makes the process much more efficient. For instance, let’s examine the following piece of code:
import React from "react"; import ReactDOM from "react-dom"; function shuffle(array) { // a function that shuffles the contents of the array } function FruitBox(props) { return ( <div className=”fruitBox”> <p>{props.fruit}</p> <input type="text" /> </div> ); } class FruitBasket extends React.Component { constructor() { super(); this.state = { fruits: ["Apples", "Oranges", "Kiwi", "Strawberries"] }; this.clickShuffle = this.clickShuffle.bind(this); } clickShuffle(e) { e.preventDefault(); this.setState({ fruits: shuffle(this.state.fruits) }) } render() { console.log(this.state.fruits); const basket = this.state.fruits.map((fruit, index) => ( <FruitBox fruit={fruit} key={fruit} /> )); return ( <div> {basket} <button onClick={this.clickShuffle}>Shuffle Basket</button> </div> ); } } const rootElement = document.getElementById("root"); ReactDOM.render(<FruitBasket />, rootElement);
The above example renders four instances of <FruitBox> which displays a paragraph tag with the name of fruit from the state variable ‘fruits’ and it also displays an input box.
When the key property is NOT set : Type something in any input box and hit the shuffle basket button. You’ll notice the names of the fruits switch places in the list because the array shuffles. But the input box where you typed stays in the same place. This happens because React is unable to keep track of individual nodes. When the array updates, the updates are applied selectively to the paragraph node elements where the update is supposed to show up.
But we would ideally want the entire <FruitBox> instance including the edited input box to move along to a new position as the array shuffles. This can be done by including a unique value to the key property. In the code above, we’ve used the name of the fruit as the key value. You should not use the Array’s index value as it recomputes when the Array updates and it naturally does not reflect a 1-1 relationship with the individual element’s contents.
React apps use the Virtual DOM and as a result, direct access to a component’s DOM element is not advised. But for special cases, if you do need access, then the ‘ref’ property may be used. Here’s an example:
class MyButton extends React.Component { constructor() { super(); this.buttonRef = React.createRef(); } componentDidMount() { this.buttonRef.current.innerText = "Register Now!"; } render() { return (<button ref={this.buttonRef}>Submit</button>); } }
In the example above, we’re first creating a new reference and then we’re using it in the componentDidMount method to change the button’s text. Refs may be used in special cases such as controlling media playback, setting focus and integration with third party DOM manipulation libraries. For everything else, even if it tends to solve a problem, one must steer clear of refs in favour of React specific techniques such as using state to cause updates/change to elements.
The value of an input box can be accessed in an onChange event handler using event.target.value. Here’s an example:
class MyInputBox extends React.Component { constructor() { super(); this.state = { message: ‘’ } this.onChangeHandler = this.onChangeHandler.bind(this); } onChangeHandler(event) { this.setState({ message: event.target.value }); } render() { return (<div> <p>{this.state.message}</p> <input type="text" onChange={this.onChangeHandler} /> </div>); } }
In the above example, whenever the user types in the box, the onChangeHandler function receives as synthetic event which can be used to access the current value of the input box. Here it is being used to update a state variable ‘message’, which is being rendered in a paragraph tag. As the user types, the text updates.
Major lifecycle methods include :
To intercept errors in a child component such as during rendering, we can implement the static getDerivedStateFromError(error) method in the parent. The purpose of this method is to update/restore the state so that a fallback interface or component may be displayed. This method should return an object that contains one or more state variables with their value set. There is no need to run the setState function here.
Though not originally so, React has evolved into a renderer agnostic library. This means that the React core is not concerned with the target medium as far as the final rendered outcome is concerned. This has allowed the creation of React Native where there is no DOM and yet you can use React to render native components on Android and iOS.
Having said that, the ReactDOM package offers DOM specific methods to React. You would use this when working on web applications that need to render in a browser. One of the most commonly used functions from this library is the ReactDOM.render() which is used to render the top most component in the DOM. Components themselves should not have the need to use functions from ReactDOM directly, however some, like the ReactDOM.findDOMNode() may be used in fringe cases.
Another function from this package is hydrate() which is used to refill a DOM container with pre-rendered HTML from a React application. This is achieved by using the ReactDOMServer package to pre-render an application before injecting it into the DOM.
HTML elements such as input fields, textarea and other form elements maintain their own internal state in the DOM. This lets the users type and set a value for instance. In a React application, the concept of state is central to a component and not to individual elements. In a React app, the state should be the central source of truth for everything. This also means that interactive components and elements must derive their contents from the state and also update that state when the user updates the field’s values. This ensures a synchronised state and prevents situations where the input element’s contents may differ from its state variable in the component. This way, React and its state have full control over the element/component. Such components are known as controlled components because their value is under full control of the React component’s state.
To implement this, a component simply derives its value from a state variable and when the user changes the component’s value, it is written back to the state. This creates a two way binding between the component and a state variable.
The following example illustrates a controlled component.
class MyInputBox extends React.Component { constructor() { super(); this.state = { message: '' } this.onChangeHandler = this.onChangeHandler.bind(this); } onChangeHandler(event) { this.setState({ message: event.target.value }); } render() { return (<div> <input type="text" value={this.state.message} onChange={this.onChangeHandler} /> </div>); } }
In the example above, the input field in the render function is a controlled component because its value is set to a state variable named message and the onChange event handler is designed to update this state variable, thereby creating a 2 way binding. The state variable ‘message’ is thus the single source of truth and controls the component.
React uses the value of the key property as a way of identifying the component instance in the Virtual DOM. This is critically important if the list has to update over time. The most important thing about the ‘key’ value is that it must be unique and not something that mutates over time. Otherwise the relationship between the contents of the component and the key value cannot be established. The index value of an Array recomputes and changes as the Array mutates over time and hence the index value can never reflect a true relationship with the list item’s contents.
Therefore, the index value should never be used as a ‘key’.
Initial state refers to pre-configured value(s) of state variables when the component renders for the first time in the app. This can be easily done using props. Here’s an example:
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { message: props.message } this.onClickHandler = this.onClickHandler.bind(this); } onClickHandler() { this.setState({ message: this.state.message.split('').reverse().join('') }); } render() { return (<div> <p>{this.state.message}</p> <button onClick={this.onClickHandler}>Reverse Message</button> </div>); } }
You can use this component like so:
<MyComponent message=”This is a message” />
The prop ‘message’ here sets an initial value of the message state variable. Once the component renders, the ‘Reverse Message’ button can be pressed to reverse the value of the message variable in the state.
Conditional rendering can be implemented easily by using simple conditional statements like the ‘if’ statement. Here’s an example:
function LogOut() { return (<p>Logout now</p>); } function LogIn() { return (<p>Login now</p>); } function UserControl(props) { if(props.isLoggedIn) { return (<LogOut />); } return (<LogIn />); } ReactDOM.render(<UserControl isLoggedIn={false} />, document.getElementById("app"));
In the example above, the <UserControl /> component features a prop called isLoggedIn which can be set to true or false. Internally, it conditionally renders either <LogIn /> component or <LogOut /> component based on the value of the isLoggedIn prop.
The create-react-app is a command line utility that can be installed to easily bootstrap a React application. It implements webpack to setup a build system which not only makes it easy to develop React apps but also offers features like hot reloading, asset and spreadsheet management, the ability to implement code splitting and several other features that are recommended by the React team. When the app has been built, the developer can generate a final production build which is highly optimised and ready for deployment on the server. The create-react-app is the officially recommended way for building React apps.
Traditionally and in the past, when using nested components, the only way to pass down configuration and data to child components has been props. So, the parent component would set a prop on child component and the child would set a prop on its child to pass in data. This becomes a problem especially when a piece of configuration is required globally across multiple components in the application tree/structure. In this case, mapping a top to down pipeline using props can be quite a challenge. Imagine the name of a user who’s logged in. Once logged in, this name may be needed in many components.
One of the newer ways to solve this problem is to use context where you set a context variable with the value that you wish to use on descendent components. A context provider then wraps the child component hierarchy and any component in that tree can then become a consumer and fetch the value from the context.
In this first code example, we’re using props to pass down a state variable called user which is ultimately consumed by a component called <UserBox />. In doing so we have to pass through <Dashboard /> because that is how we would be able to pass the value down the tree.
Using Props class MyApp extends React.Component { constructor() { super(); this.state = { user: "John Doe" }; } render() { return ( <div> <Dashboard user={this.state.user} /> </div> ); } } function Dashboard(props) { return ( <div className="dashboard"> <h1>Dashboard</h1> <UserBox user={props.user} /> </div> ); } function UserBox(props) { return <h3>{props.user}</h3>; }
Instead of charting a downstream channel through props, we can instead use Context as shown in the following code segment:
const UserName = React.createContext(""); class MyApp extends React.Component { constructor() { super(); this.state = { user: "John Doe" }; } render() { return ( <div> <UserName.Provider value={this.state.user}> <Dashboard /> </UserName.Provider> </div> ); } } function Dashboard() { return ( <div className="dashboard"> <h1>Dashboard</h1> <UserName.Consumer> {value => <UserBox user={value} />} </UserName.Consumer> </div> ); } function UserBox(props) { return <h3>{props.user}</h3>; }
Here, we have a Provider on the parent. The child component is then wrapped in a context consumer which returns a component. The consumer subscribes to the provider and all consumers get an updated value when the provider updates it.
The value of a state variable is used across many components in a large application. That is why, in the hierarchy of components, the state should be positioned at the closest parent of components so that no additional mechanism is required to synchronise data across components. The most fundamental data flow strategy in React is the top-down data flow, where data flows from the top most component, down through the props/context to descendent child components. That is why it makes logical and practical sense to hoist the state at the parent or the nearest possible parent.
In a component’s render function, you cannot have multiple top level components as adjacent nodes. You would need to wrap them inside a div or some other form of top level component to make it work. This often introduces an extra DOM node in the form of a wrapper div or some other element, which is not required by the layout but is merely introduced as a necessity. In many instances, it can also lead to invalid HTML. Such as introducing a div inside an UL node to wrap a set of LI nodes. Fragments help you resolve this problem. You can enclose your top level sibling nodes inside the fragments component (as shown below) and an unnecessary DOM node is not introduced.
THE PROBLEM
function MyApp() { return ( <ul> <Fruits /> </ul> ); } function Fruits() { return ( <li>Apples</li> <li>Strawberries</li> ); }
This code is invalid because adjacent and top level <li> nodes in the Fruits component are not permitted in JSX. To solve this, you’d be tempted to wrap them in a div but that would render invalid HTML.
React allows you to solve this problem using Fragments as shown below:
function MyApp() { return ( <ul> <Fruits /> </ul> ); } function Fruits() { return ( <> <li>Apples</li> <li>Strawberries</li> </> ); }
By wrapping the adjacent LI nodes in a pair of fragment components <></>, an unwanted DOM node won’t be inserted and you would be able to render multiple adjacent components/elements easily.
A generally ideal place to fetch data from an API call is the componentDidMount() lifecycle method. This method is executed when the component has been inserted into the DOM tree and thus has been mounted. At this time, a network call to fetch data can be done and upon successful completion, the data may be used to update the state thereby causing the component to re-render and display the fetched data.
To fetch the data, a commonly used library is axios. However, something as fundamental as the fetch API may be used. An example using Axios is shown below.
class Users extends React.Component { constructor() { super(); this.state = { users: [] }; } componentDidMount() { axios.get("https://jsonplaceholder.typicode.com/users").then(data => this.setState({ users: data.data }) ); } render() { return ( <div> {this.state.users.map(user => ( <UserBox data={user} key={user.id} /> ))} </div> ); } } function UserBox(props) { return ( <div> <h2>{props.data.name}</h2> <h3> {props.data.email} | {props.data.phone} </h3> <hr /> </div> ); }
The example above brings in data from an API and displays that in the app.
Lazy loading refers to deferred loading of components to a time when they’re actually needed in the application. Generally, when we use webpack as the build tool for bundling a React app, such as when using the create-react-app tool, the build process would end up bundling all components into one big bundle file which loads in full before the app can execute. While this is usually fine, it isn’t a good practice when your app is large or has grown large over time, as you’ve added new features and capabilities.
In such a case, an ideal situation would be to split the code across multiple files/components and load them only when they’re needed in the application. This is what is called ‘lazy-loading’ a component.
React’s Suspense API provides us with such a toolkit that makes it a breeze to lazy load components. Here’s an example:
const DataSheet = React.lazy(() => import('./DataSheet')); function Loading() { return (<div className="loading">Loading Data...</div>); } class MainComponent extends React.Component { constructor() { super(); } render() { return ( <Suspense fallback={<Loading />}> <DataSheet /> </Suspense> ); } }
In the example above, the <DataSheet /> component is being loaded from an external file using an import statement. When you build this project, webpack sees the import statement and splits the bundle so that <DataSheet> resides in a separate file and not bundled with main application.
When the <MainComponent> is rendered, the <DataSheet> component will be dynamically loaded and while it is being loaded, a fallback <Loading> component will be displayed. The lazy loading is made possible using React.lazy() which is part of the Suspense API.
Strict Mode is the name of a special tool that developers can use to highlight a set of potential problems with React components. This only works in development and raises warnings about unsafe lifecycle methods which are not recommended, legacy String ref api and various other deprecated and legacy API usage. More and more warnings will be added as React continues to evolve. Using this tool involves wrapping one or more components in the <React.StrictMode></React.StrictMode> component as shown below:
class MainComponent extends React.Component { constructor() { super(); } render() { return ( <React.StrictMode> <DataSheet /> </React.StrictMode> ); } }
The <React.StrictMode> component doesn’t add any node to the DOM and is merely used to wrap components for whom you need warnings to be issued.
It is a common practice to bind class methods such as event handler functions in the constructor before using it in the render function. The example below demonstrates this practice:
class MyApp extends React.Component { constructor() { super(); this.clickHandler = this.clickHandler.bind(this); } clickHandler() { // Do something here when the button is clicked } render() { return ( <button onClick={this.clickHandler}>Do Something!</button> ); } }
In JavaScript, if you create a reference to a class method in another method, the ‘this’ context is not bound to the original function. Consider the following example of a standard JavaScript class (Not necessarily a React method):
class doSomething { constructor() { } create() { this.message = "Hey there!"; } render() { const fnRef = this.create; fnRef(); console.log(this.message); } } const doSomethingInstance = new doSomething(); doSomethingInstance.render(); // undefined
In the above example, a reference to the ‘create’ class method is created in the render method first. This breaks the original ‘this’ context of the create() method which was bound to the doSomething class instance and creates a local ‘this’ context. So, ‘this.message’ does NOT set an instance variable, and instead sets a context variable, local to the create() method. As a result, the render function is unable to access this context and hence outputs undefined. This is because in JavaScript, class methods are not bound by default.
If we first bind the function in the constructor by calling this.create = this.create.bind(this) the ‘this’ context is bound correctly and preserved and hence ‘this.message’ refers to the ‘this’ context of the class instance.
If you do not wish to bind the methods first, then you can also use arrow functions as shown below:
class MyApp extends React.Component { constructor() { super(); } clickHandler() { // Do something here when the button is clicked } render() { return ( <button onClick={e => this.clickHandler(e)}>Do Something!</button> ); } }
In some cases, it may be required to force a re-render on a component even when the state/props have not updated/changed. This can be achieved by using this.forceUpdate() in the component. The following example creates a simple clock that uses forceUpdate to re-render every 1 second.
class MyClock extends React.Component { constructor() { super(); } componentDidMount() { this.intervalTimer = setInterval(() => this.forceUpdate(), 1000); } componentWillUnmount() { clearInterval(this.intervalTimer); } render() { const time = new Date().toLocaleTimeString(); return <h1>{time}</h1>; } }
The above example is not necessarily the most efficient one, but is an example of how forceUpdate() may be used. While forceUpdate() works, it is not recommended and should only be used when you are unable to update state/props due to some logical reason.
React components are subclasses of the React.Component parent class. If you have a constructor() in the component class, then you need to call super() to enable the class to access methods from the parent class as well as for initializing the ‘this’ context variable. While a constructor is NOT necessary for class based components, you do need to initialize one if your component will use props or state. When using props, it is mandatory to initialize the constructor with super(props) or else, neither the ‘this’ nor ‘this.props’ would be available inside class methods. The invocation of super() in the constructor of subclasses is required as per ES6.
Function components are essentially functions that return a React component, written typically using JSX. Function components can also accept props but they do not offer lifecycle methods and state management/manipulation. To solve this problem, React has introduced an experimental API called ‘hooks’ which lets your function components ‘hook’ into React features. To implement state in a function component, we can use the ‘useState’ hook as shown in the example below:
import { useState } from "react"; function MyApp() { const [greeting, reverseGreeting] = useState("Hello there!"); return ( <div> <h1>{greeting}</h1> <button onClick={() => reverseGreeting( greeting .split("") .reverse() .join("") ) } > Change Text </button> </div> ); }
In the example above, we’re using the experimental useState hook from React 16.7.0-alpha.2. The useState() allows us to initialize a state variable ‘greeting’ and a function ‘reverseGreeting’ that will modify the state. We can call the reverseGreeting function to update the state in the component.
When using props, we are dependent on the consumer to ensure correct data types. Having said that, we do have a way to validate props and raise warnings if the incoming data does not adhere to the correct data type. This, however only throws warnings in the development phase and has no effect in a production build.
import PropTypes from "prop-types"; function MyApp(props) { return ( <div> <h1>{props.name}</h1> <h4>{props.age} years old</h4> </div> ); } MyApp.propTypes = { name: PropTypes.string, age: PropTypes.number };
We begin by importing PropTypes from the prop-types package which brings in a number of prop validators. After defining your component, we can then configure the propTypes object to declare every prop and consequent validators as shown above.
This will raise warnings on the console if the incoming data type does not match the validator type. Custom validators can also be created.
Refs are used to access the underlying DOM node of a component/element. While refs should ideally be avoided, there are times where you might need to access the DOM node directly. When authoring components/libraries for public distribution and use, it often becomes necessary to offer access to a component’s internal DOM nodes using ‘refs’. Forwarding refs allow you to offer a ref to an internal child component/node.
Here’s an example:
class Dialog extends React.Component { constructor(props) { super(props); this.state = { showDialog: true }; } closeDialog() { this.setState({ showDialog: false }); } render() { const { forwardedRef, ...rest } = this.props; if (this.state.showDialog) { return ( <div className="dialog"> <a className="close-btn" ref={forwardedRef} onClick={() => this.closeDialog()} > Close Dialog </a> {this.props.children} </div> ); } else { return null; } } } const DialogComponent = React.forwardRef((props, ref) => ( <Dialog {...props} forwardedRef={ref} /> )); class MyApp extends React.Component { constructor() { super(); this.buttonRef = React.createRef(); } componentDidMount() { this.buttonRef.current.innerText = "Close this Dialog"; } render() { return ( <DialogComponent ref={this.buttonRef}> <h1>This is a dialog box!</h1> </DialogComponent> ); } }
In the example above, the Dialog component includes a button (a hyperlink) that is being turned into a ref which we’re forwarding from <DialogComponent> using React.forwardRef(). We can then access and modify the hyperlink node from whichever component that uses the <DialogComponent>.
Here we’re changing the innerText property of the hyperlink from the <MyApp /> component.
React offers three ways to ensure a component only re-renders when its props update and not otherwise.
The first of these methods involves the use of the shouldComponentUpdate() method that is available in class based components. By using this method, we can control the re-renders for a given component based on whether state variables or props have indeed updated or not. The example below will only permit re-renders when the message prop updates. While this works, the use of the shouldComponentUpdate() method incurs a performance cost and must be avoided wherever possible.
class Dialog extends React.Component { constructor(props) { super(props); } shouldComponentUpdate(nextProps, nextState) { return this.props.message !== nextProps.message; } render() { return <div className="dialog-box">{this.props.message}</div>; } }
The second method, also available for class based components is the use of React.PureComponent. A Pure Component does not implement a configurable shouldComponentUpdate() method but implements a built-in shallow comparison of the component’s state and props so components will only re-render if the props or state actually changes. The comparison however is shallow and complex objects will not deliver the desired behaviour. The following example implements a pure component.
class Dialog extends React.PureComponent { constructor(props) { super(props); } render() { return <div className="dialog-box">{this.props.message}</div>; } }
The third method can be used with function components which do not implement an internal shouldComponentUpdate method. In this method, we use the React.memo() higher order component to implement basic memoization. This is, just like the PureComponent, only does a shallow comparison and will not deliver desired results on complex prop/state objects.
function Dialog(props) { return <div className="dialog-box">{props.message}</div>; } const DialogComponent = React.memo(Dialog);
If the default memoization behaviour is not preferred, then it can be customised like the shouldComponentUpdate() method as shown below:
function Dialog(props) { return <div className="dialog-box">{props.message}</div>; } const DialogComponent = React.memo( Dialog, (prevProps, nextProps) => prevProps.message === nextProps.message );
It is indeed possible to selectively render as component in a location other than the nearest parent’s DOM node by using Portals. A portal, as the name implies is a way to direct the rendering of a component to a DOM node of choice irrespective of the parent component’s DOM target. Here’s an example:
class MyApp extends React.PureComponent { constructor() { super(); this.state = { count: 10, timeout: false }; } componentDidMount() { setTimeout(() => { this.setState({ timeout: true }); }, this.state.count * 1000); } render() { if (this.state.timeout) { return ReactDOM.createPortal( <Dialog message="Timeout!!" />, document.getElementById("dialog") ); } else { return <h1>This message will self destruct!</h1>; } } }
In the example above, we implement as simple countdown that counts down from 10 seconds. When it reaches 0, an instance of the Dialog component is rendered into a div with the id ‘dialog’ which is different from the node where the MyApp component renders. We’ve used the createPortal() method from the ReactDOM package to achieve this functionality.
A higher order component is a function that accepts a component and returns a valid component after augmenting it with custom logic. This is not something that is React specific and is merely a pattern which let’s developers write logic that can be shared across multiple components. The react-router package as an example offers higher order components. Here’s an example of a higher order component:
function Dialog(props) { return <div className="dialog-box">{props.message}</div>; } function Message(props) { return <h1>{props.message}</h1>; } function withCountdown(WrappedComponent, countdownSec = 10) { class SelfDestruct extends React.Component { constructor(props) { super(props); this.state = { timeout: false }; } componentDidMount() { setTimeout(() => { this.setState({ timeout: true }); }, countdownSec * 1000); } render() { if (this.state.timeout) { return ReactDOM.createPortal( <Dialog message="Timeout!!" />, document.getElementById("dialog") ); } else { return <WrappedComponent {...this.props} />; } } } return SelfDestruct; } const MyApp = withCountdown(Message, 5); const rootElement = document.getElementById("root"); ReactDOM.render( <MyApp message="This message will self destruct soon!" />, rootElement );
In the example above, the withCountdown() higher order function takes in a component and a value in seconds. The component is then shown for the set duration in seconds, after which a timeout message is displayed. The countdown logic is wrapped up inside the withCountdown() HOC and the consumer need not be concerned about the internals. This makes HOCs a very powerful design pattern for writing shareable and reusable logic in React apps.
The code above will output 40 as the value of the state variable ‘count’. This is because, this.setState() is asynchronous and it is accessing this.state.count which will always reflect the ‘rendered’ value of the state variable ‘count’. When the component renders, it reads the original value of this.state. This is why, the second setState() invocation above also ends up reading the value of this.state.count as 10, which is multiplied by 4 to result in 40.
If you’re seeking 80 as the result, you have to use update functions with setState() as shown below. In this case, all updater functions are batch processed and the state updated, only after the batch has been computed in full. Instead of this.state, we’re accessing state which is the updater function’s internal value and does not depend on rendered state values.
class MathsBrain extends React.Component { constructor() { super(); this.state = { count: 10 }; } componentDidMount() { this.setState(state => { return { count: state.count * 2 }; }); this.setState(state => { return { count: state.count * 4 }; }); } render() { return <div>Count: {this.state.count}</div>; } }
The revised component above, implements updater functions instead of accessing this.state.count which will always reflect the rendered value of the state.
Submitted questions and answers are subjecct to review and editing,and may or may not be selected for posting, at the sole discretion of Knowledgehut.