Quick React.js Refresher #1
I haven’t been very fond of developing frontend apps since trying it out, especially because of how different JavaScript is to what I’ve been used to, namely procedural or object-oriented programming languages. But I think I’ve come round to accepting it now, after trying React.
I suppose Angular didn’t really click with me, what with all the ngModules, and React is quite different from that, being much simpler (and barebones). So here’s a complete (pretty much) write-up of where I’ve gotten so far with React, in case I need a refresher when I have to come back to it.
All about them components
A (pure) React app consists of components cobbled together to create an HTML view with bits and pieces of JavaScript working behind it. There are two “types” of components - class components and function components - but for all purposes that I can find, they’re pretty much the same now functionality-wise. The overall structure of one are all rather similar as well:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { name: "Henry" };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ name: "John" });
}
render() {
return (
<div>
<h1>Hi, I'm {this.state.name}</h1>
<button onClick={this.handleClick}>Click me</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
The component above renders a heading 1 with the string “Hi, I’m Henry” and a button that when clicked changes the heading text to “Hi, I’m John”. Note the imports on the first two lines for importing the react
and react-dom
modules to be used. Also note the component has to extend the React.Component
. This component has its own state
but can also take in props
, if rendered by a parent component. The state
object has only one item: a string name
which is passed to be rendered. Meanwhile, the button receives an event handler handleClick
, which changes the name to “John”. This uses the setState()
function. The event handler handleClick
also has to be bound to the App
component, as done in the constructor.
The component is then rendered using ReactDOM.render()
to the corresponding DOM with id='app'
on an HTML file using this component. It should be noted that the return statement inside the component’s render
function should have only one outermost JSX tag.
Now, the above can also be written as a function component, which basically is a component written as a function. Before React 16.3, function components had no state of its own but with the introduction of “Hooks”, it seemingly (as far as my inexperienced understanding goes) can do what a class component can.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
function App(props) {
const [name, setName] = useState("");
useEffect(() => setName("Henry"), []);
const handleClick = () => {
setName("John");
}
return (
<div>
<h1>Hi, I'm {name}</h1>
<button onClick={handleClick}>Click me</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('app'));
The function component above does the same thing as the class component before it. Note the absence of the render()
function inside the function itself. Also, two hooks are shown here: useState
and useEffect
. useState()
is for creating the state variable name
(initialized to an empty string) and its setter setName
, both of which are returned by the function. Note that the parameter for useState()
is what the variable is initialized to and helps any code reader determine the data the variable holds.
useEffect()
generally controls how the component state changes on first render and afterwards. Here, it acts like a constructor for name
, setting it to “Henry”. Note the second parameter passed to the function is an array, called the “dependency array”, which executes the callback function only when:
- If the dependency array is empty, the callback function is run once on first render. This acts like the
componentDidMount
function for class component lifecycles. - If the dependency array has variable names in it, the callback function is run every time either one of the variables change. This acts like a combination of
componentDidMount
andcomponentDidUpdate
- If the dependency array is not passed to
useEffect
at all, the callback function is run every time the component is rendered.
useEffect()
can also act like componentWillUpdate()
for a class component lifecycle function by returning a method to stop or unsubscribe the effect being called. There’s probably a lot more explanation required for more in-depth use of useEffect()
, so one can go here if need be.
Now that’s largely it for the composition of components. As far as I’m concerned, I don’t really see the reason to use class components anymore, as function components are more concise to write (although they can be more obfuscating for newcomers).
All about them callbacks (or just, some more examples)
I’d say the hook functions rely very heavily on callback functions. As an example, especially when modifying state based on the current:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
function App(props) {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prev => prev + 1); // setCount uses the callback to add 1 to the current count
}
return (
<div>
<h1>Click counter: {count}</h1>
<button onClick={handleClick}>Increment</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('app'));
Example: counting the clicks on the button
Array functions can also be particularly useful when working with lists and can be used pretty effectively with JSX:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
function App(props) {
const [curr, setCurr] = useState("");
const [arr, setArr] = useState([]);
const handleChange = (event) => {
setCurr(event.target.value);
}
const handleSubmit = () => {
// set the array to include the current names as well as the new input
setArr(prev => [...prev, curr]);
setCurr("");
}
// note the use of .map to return a bunch of list elements to <ul>
return (
<div>
<form>
<label>Name: </label>
<input type='text' value={curr} onChange={handleChange}/>
<input type='submit' value="Submit" onSubmit={handleSubmit}>
</form>
<ul>
{arr.map(name => <li>name</li>)}
</ul>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('app'));
Example: adding names to a list
Parent functions can also be passed to child components, so the child can directly affect the parent’s state.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
function Parent(props) {
const [name, setName] = useState(0);
// this function sets the name to be displayed
const handleClick = (value) => {
setName(value);
}
// pass function handleClick to child
return (
<div>
<h1>Name: {name}</h1>
<Child onClick={handleClick} />
</div>
);
}
function Child(props) {
const [curr, setCurr] = useState("");
const handleChange = (event) => {
setCurr(event.target.value);
}
// set the passed in function to handle submit
const handleSubmit = () => {
props.onClick(curr);
}
return (
<div>
<form>
<label>Enter name: </label>
<input type='text' value={curr} onChange={handleChange}/>
<input type='submit' value="Submit" onSubmit={handleSubmit}>
</form>
</div>
);
}
ReactDOM.render(<Parent />, document.getElementById('app'));
Example: more convoluted way to get a name from a submitted form (only for demonstration purposes
I suppose this is all there is to note down for now. I’m working on a little React project to flesh out my knowledge on it further, deploying the app a bit at a time. I’ve also got to follow up on the previous OpenGL piece, otherwise naming all of these with numbers is going to be redundant. That said, I probably will continue working on learning React; there’s still routers and more to come.