React And JSX: Main Concepts

chloroplastic
Learning to code bit by bit
7 min readDec 21, 2018
Photo by Artem Sapegin on Unsplash

React is a JavaScript library that can be used in order to improve the UI (User Interface) of your web apps, and it is especially useful for building complex projects. It combines HTML with JavaScript in its own markup language, JSX, allowing you to write HTML code directly in JS (using camelCase). Anything in curly braces in the JSX portion of your file will execute as normal JS. However, JSX code is not the same as JavaScript code, and in order to be valid it therefore needs to be compiled.

To render a React JSX element or component to the DOM you can use the render function: ReactDOM.render(elementToRender, targetDOMNode). As in regular JS, you can select your target DOM node via document.getElementById().

React has its own representation of the DOM. To be able to place your JSX code into it you can use the render function, like so: ReactDOM.render(JSX, document.getElementById('root')).

One important difference between HTML and JSX is that, in JSX, you need to define classes with the word className instead of class. It’s in fact important to remember that React is a JavaScript library, and in JS class is a reserved word. This is also true for other HTML attributes and events.

Another difference with HTML, is that in JSX you always need to close tags: this means, for instance, that tags such as <br> or <hr> will need to be written as <br></br> or simply <br />. In JSX every single one of your elements can be written with a self-closing tag.

Components

Everything in React is a component, and each component can be seen as an individual part of your complete UI. You can create components in two different ways:

  • Using JavaScript functions, to create a stateless functional component. All your function names in React need to have their first letter capitalised, and they need to return JSX (or null). You will also be able to pass props as the function argument, and use it in any element you return (see below).
  • Using the class syntax to extend other existing parent classes. Within that class you will be able to use the render() function to return JSX.

Each component can only return one DOM element: if, for instance, you try to return a pair of <h1>s, you will get an error. To avoid this problem you can wrap the two elements within a single parent tag (e.g. <div>…</div>, <React.Fragment>…</React.Fragment>, <>…</> ) and then return said element. This will create a composition of multiple components, and will render all the ChildComponent s within their ParentComponent:

return(
<ParentComponent>
<ChildComponent1 />
<ChildComponent2 />
<ChildComponent3 />
<ChildComponent4 />
</ParentComponent>
)

To render a class component to the ReactDOM the syntax is similar to the one mentioned earlier to render regular elements, except this time you will need to put the < /> tag around the component to render: React.DOM.render(<ComponentToRender />, targetDOMNode) .

An example:

class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
// JSX code that your component will render
);
}
};
React.DOM.render(<MyComponent />, targetDOMNode)

If you don’t provide a constructor for your component, React will automatically provide one for you.

React components have a series of special methods that allow you to perform actions at a specific point in their own “lifecycle”. Those include: componentWillMount() , componentDidMount() , componentWillReceiveProps() , shouldComponentUpdate() , componentWillUpdate() , componentDidUpdate() , componentWillUnmount() . They can be extremely helpful, especially if you want to set up functions as soon as your components are rendered the first time (mounted), are updated, or to free up resources by clearing those functions as soon as your components are removed (unmounted).

State

A component’s state allows you to keep track of changes in some of your app’s data and then render a different UI conditionally dependent on those changes. To create state in a component you need to declare a state property on the component class constructor and set it to an object: this.state = { // your code }. Doing so will initialise the component with state whenever it is created.

There are two different ways to access state in a component:

  1. You can write { this.state.propertyName } in your return statement.
  2. You can use the render()method (right before the return statement) as a place to write pure JavaScript; declaring variables, functions, accessing/assigning data, etc. You can then access to those variables in the return statement, by writing their names in curly braces. For instance: const myProperty = this.state.propertyName;{ myProperty }.

The state property can always be updated, accessed, or passed as props to a component’s child. One component, though, can never access another component’s state: each state is local.

Another important thing to note is that you can never change the state directly: to change a component’s state you can use the setState method, passing in an object with key/value pairs (containing the state properties and their values), or a function. You can update different state variables independently, with separate setState calls. For instance: this.setState({ propertyName: "New value" });. Once you set a new state, the change will be reflected to all the child components as well. If you need to reference state in your state update, make sure to use a state updater function.

If a class method needs to access an internal state (this.state), you always have to make sure to bind this to the instance of the class (as ES6 class methods are by default not bound). A way to do that is to bind this to the constructor method by including in it: this.myMethod = this.myMethod.bind(this);. Another solution consists in writing your method using arrow functions, since they don’t have their own this value, and they preserve its value from the outer (parent) element: they lexically bind their context by default.

You can choose to put your state within the constructor as this.state, or outside of it as simply state; the same goes for other additional methods you might include.

Props

In React, you can pass properties (props) from parent components to child components within the render method like so: <ChildComp myPropriety={ this.state.propertyName }/>. The parent’s state value of propertyName becomes the child's this.props.myProperty, and can then be accessed in the child component. When doing so with stateless functional components you have to make sure you’re passing props as the argument of your function. Props can be anything: strings, arrays, and even methods (event handlers). You can overwrite whatever is in the props by writing said value right after {...props}.

You can change a child component’s prop through child events and parent callbacks. For instance, you could add an onChange event on the child:

<ChildComp myProperty={this.state.propertyName} onChange={this.handleChange} />

And then the parent would use the event handler to update its state:

class ParentComp extends ReactComponent {
constructor(props) {
super(props);
this.state = { // your code }
// add this.handleChange = this.handleChange.bind(this); unless you're using arrow functions
}
handleChange = event => {
event.preventDefault();
this.setState({
propertyName: "New value"
});
}


render() {
return( // your code );
}
};

In React it is a convention to use onEvent names for props which represent events, and handleEvent for the methods which handle the events.

You can also set default props, or props that will get assigned to a component if props are undefined (if you don’t explicitly provide any value). To do so you can write: MyComponent.defaultProps = { myPropriety: "Propriety Value" }. Default props can be then overridden by explicitly setting the component’s prop values.

PropTypes are type-checking features that can be used to specify the type of props that you expect. For instance, you can set them to be an array, a function, and so on. If the data ends up being of another type, you will get an error. To use PropTypes, the syntax is similar to the one you use by default: MyComponent.propTypes = { myPropriety: PropTypes.func }. Your PropTypes can be set to func, bool, number, array, and so on. It’s important to note that PropTypes is imported independently from React: import React, { PropTypes } from "react";.

.isRequired is a method that can be added to a PropType to specify that a certain property is required for your component. If you don’t provide said property, you will get an error.

When rendering a list, every item that gets mapped should have a unique key prop among its siblings. This will allow you to specifically keep track of each element associated with your data by giving each element a special identity. Using indices as keys is fine as long as your items are never re-ordered, deleted, or inserted in the middle.

Notes:

  • In React the data flow is unidirectional: this means that state flows in a single direction, from the stateful parent component to child components, allowing each child component to only receive the state data that is needed.
  • In React, you can separate state management and UI rendering in different parts of your code, which allows you to easily manage complex applications. This can be done by breaking down complex stateful applications into just a few stateful components; the rest of the components will then receive state from their parent (as props), and render a specific UI depending on that state.
  • If you need to render React on the server-side, you should use the renderToString() method: this prevents you from having a relatively empty HTML file and a huge JS file, which would imply a slower page load and would make it harder for your app to be found by search engines. You can use this method like so: ReactDOMServer.renderToString( < ComponentName /> ).
  • When dealing with external APIs, you can use the componentDidMount() method mentioned earlier to set the state with the data returned by the API as soon as that component gets mounted. This will automatically trigger an update in your UI as soon as the data is received. In order to add functionality, you could also add event listeners to it (document.addEventListener()), remembering to then remove them with document.removeEventListener() in componentWillUnmount().
componentDidMount() {
fetch("https://path-to-the-resource-you-want-to-fetch.com")
.then(response => response.json())
.then(data => {
this.setState({ // your code });
}
}

Useful links

--

--