JSX is a form of markup used in React. It looks very similar to HTML, but is converted to JavaScript behind the scenes. You are not required to use JSX, but JSX makes it easier to write React applications.
const welcomeElement = <h1>Hey man!</h1>;
ReactDOM.render(welcomeElement, document.getElementById('root'));
const welcomeElement = React.createElement('h1', {}, 'Hey man!');
ReactDOM.render(welcomeElement, document.getElementById('root'));
You can learn more about how JSX works from this blog post.
Components are the heart and soul of React. Most simply component is just a function that accept some properties and return a UI element.
Components take raw data and render it as HTML in the DOM. It describes what to render. They utilize properties (props for short) and states which contribute to this raw data. Both props and states are plain Javascript objects.
const Button = (props) => {
const { label, onClick, loading } = props;
return <button onClick={(e) => onClick(e)} disabled={loading}>{label}</button>
}
With React, you can make components using either classes or functions. Originally, class components were the only components that could have state. But since the introduction of React's Hooks API, you can add state and more to function components.
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
State : This is data maintained inside a component. It is local or owned by that specific component. The component itself will update the state using the setState function.
Props : Data passed in from a parent component. props are read-only in the child component that receives them. However, callback functions can also be passed, which can be executed inside the child to initiate an update.
The difference is all about which component owns the data. State is owned locally and updated by the component itself. Props are owned by a parent component and are read-only. Props can only be updated if a callback function is passed to the child to trigger an upstream change.
The state of a parent component can be passed a prop to the child. They are referencing the same value, but only the parent component can update it.
You can think of React lifecycle methods as the series of events that happen from the birth of a React component to its death.
- Mounting – Birth of your component
- Update – Growth of your component
- Unmount – Death of your component
The render() method is the most used lifecycle method. You will see it in all React classes. This is because render() is the only required method within a class component in React.
As the name suggests it handles the rendering of your component to the UI. It happens during the mounting and updating of your component.
Now your component has been mounted and ready, that’s when the next React lifecycle method componentDidMount() comes in play.
componentDidMount() is called as soon as the component is mounted and ready. This is a good place to initiate API calls, if you need to load data from a remote endpoint.
This lifecycle method is invoked as soon as the updating happens. The most common use case for the componentDidUpdate() method is updating the DOM in response to prop or state changes.
You can call setState() in this lifecycle, but keep in mind that you will need to wrap it in a condition to check for state or prop changes from previous state. Incorrect usage of setState() can lead to an infinite loop.
As the name suggests this lifecycle method is called just before the component is unmounted and destroyed. If there are any cleanup actions (removing event listeners, etc) that you would need to do, this would be the right spot.
- shouldComponentUpdate()
- static getDerivedStateFromProps()
- getSnapshotBeforeUpdate()
You can learn more about the above lifecycle metheds from this page.
In React we can manage application state using different ways, most used ways are:
- Local Component State (setState)
- Hooks
- Redux
- Context
React itself provides some useful methods for setting component states using setState() and adding a “local state” to a class.
With this option, you can call these methods for your components. setState() tells React that this component and its children (sometimes delayed and grouped into a single batch) should be re-rendered with the most updated state, often bases on user-triggered events. setState() will always lead to a re-render as long as an update is available.
state = {
test: 1
}
inc() {
console.log('before: ' + this.state.test);
this.setState({
test: this.state.test+1
});
console.log('after: ' + this.state.test);
}
render() {
return <button onClick={this.inc}>Click to update</button>
}
// click!
before: 1
after: 1
// click!
before: 2
after: 2
The new stable hooks introduced in React 16.8.0. let you create a custom hook to manage states inside functional components. Hooks states work pretty much like like class component states, while every instance of the component has its own state.
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
You can learn more about hooks from this page.
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language. For example, in the code below we manually thread through a “theme” prop in order to style the Button component:
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
// The Toolbar component must take an extra "theme" prop
// and pass it to the ThemedButton. This can become painful
// if every single button in the app needs to know the theme
// because it would have to be passed through all components.
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
Using context, we can avoid passing props through intermediate elements:
// Context lets us pass a value deep into the component tree
// without explicitly threading it through every component.
// Create a context for the current theme (with "light" as the default).
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
Redux also allow us to share global state across the application and provides some utility functions to update state at every level in an more organized way than Context API.
It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.
You can use Redux together with React, or with any other view library.
- Single sourceof truth: The state of your whole application is stored in an object tree within a single store.
- State is read-only: The only way to change the state is to emit an action, an object describing what happened.
- Changes are made with pure functions: To specify how the state tree is transformed by actions, you write pure reducers.
A store is an object that holds the application's state tree. There should only be a single store in a Redux app, as the composition happens on the reducer level.
dispatch(action)
is the base dispatch function described above.getState()
returns the current state of the store.subscribe(listener)
registers a function to be called on state changes.replaceReducer(nextReducer)
can be used to implement hot reloading and code splitting. Most likely you won't use it.
type Store = {
dispatch: Dispatch
getState: () => State
subscribe: (listener: () => void) => () => void
replaceReducer: (reducer: Reducer) => void
}
An action is a plain object that represents an intention to change the state. Actions are the only way to get data into the store. Any data, whether from UI events, network callbacks, or other sources such as WebSockets needs to eventually be dispatched as actions.
Actions must have a type field that indicates the type of action being performed. Types can be defined as constants and imported from another module. It's better to use strings for type than Symbols because strings are serializable.
Other than type, the structure of an action object is really up to you. If you're interested, check out Flux Standard Action for recommendations on how actions should be constructed.
A reducer (also called a reducing function) is a function that accepts an accumulation and a value and returns a new accumulation. They are used to reduce a collection of values down to a single value.
Reducers are not unique to Redux—they are a fundamental concept in functional programming. Even most non-functional languages, like JavaScript, have a built-in API for reducing. In JavaScript, it's Array.prototype.reduce().
In Redux, the accumulated value is the state object, and the values being accumulated are actions. Reducers calculate a new state given the previous state and an action. They must be pure functions—functions that return the exact same output for given inputs. They should also be free of side-effects. This is what enables exciting features like hot reloading and time travel.
Reducers are the most important concept in Redux.
Do not put API calls into reducers.
Will see the more detailed explanation of Redux in the next section.