Useful Resources & Links
We'll learn how to build our first React project locally on our machine.
Before we setup a local project, we need to use a more elaborate workflow (than just using Codepen or jsbin). It is recommended for both SPAs and MPAs.
Why?
- Optimise code: we want to ship code that is as small as possible and as optimized as possible, because that increases the performance of our app.
- Use next-gen JavaScript features: it makes our life as a developer much easier! The code is leaner,_ easier to read_, faster, less error prone and many other reasons. + compatibility reasons, we need a build workflow that actually compiles these features to support as many browsers possible.
- Be more productive, it includes next generation JavaScript features which often allow us to write more condensed code but it also includes things like CSS auto-prefixing (increase the browser support for CSS features). Or linting.
How?
- Use dependency management tool:
NPM
oryarn
. - Use a bundler: here we're going to use Webpack.
- Use compiler (next-gen JavaScript): Babel + presets.
- Use a development server.
Note: Create React App is a tool which have everything above out of the box – perfect to start a new React project :)
# create a new create-react-app project
npx create-react-app react-complete-guide --scripts-version 1.1.5
/node_modules
/public
/src
.gitignore
package.json
README.md
yarn.lock
All clear in this section.
Here is our first component. React is all about component!
import React, { Component } from 'react'; // React and Component from react library
import './App.css';
// class App extends Component
class App extends Component {
// render method is the only one which is required
render() {
// just below it is now HTML but JSX
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
</div>
);
}
}
// we need to export this component
export default App;
Working without JSX, replace it with React.createElement
.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
// return (
// <div className="App">
// <h1>Hi, I'm a React App!</h1>
// </div>
// );
const h1 = React.createElement('h1', null, "Hi, I'm a React App!");
return React.createElement('div', { className: 'App' }, h1);
}
}
export default App;
For example, class
can't be used in JSX, because JSX is JS and class
is a reserved word. Also we can't return more than one root element (solution using Fragment <></>
).
// src/Person/Person.js
import React from 'react';
const person = () => {
return <div>I'm a Person!</div>;
};
export default person;
// src/App.js
import React, { Component } from 'react';
import Person from './Person/Person';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
<Person />
</div>
);
}
}
export default App;
Components are the core building block of React apps. Actually, React really is just a library for creating components in its core.
A typical React app therefore could be depicted as a component tree - having one root component ("App") and then an potentially infinite amount of nested child components.
Each component needs to return/ render some JSX code - it defines which HTML code React should render to the real DOM in the end.
JSX is NOT HTML but it looks a lot like it. Differences can be seen when looking closely though (for example className
in JSX vs class
in "normal HTML"). JSX is just syntactic sugar for JavaScript, allowing you to write HTMLish code instead of nested React.createElement(...)
calls.
When creating components, you have the choice between two different ways:
-
Functional components (also referred to as "presentational", "dumb" or "stateless" components - more about this later in the course) =>
const cmp = () => { return <div>some JSX</div> }
(using ES6 arrow functions as shown here is recommended but optional) -
class-based components (also referred to as "containers", "smart" or "stateful" components) =>
class Cmp extends Component { render () { return <div>some JSX</div> } }
// src/App.js
import React, { Component } from 'react';
import './App.css';
import Person from './Person/Person';
class App extends Component {
render() {
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
<Person />
<Person />
<Person />
</div>
);
}
}
export default App;
// src/Person/Person.js
import React from 'react';
const person = () => {
return (
<p>I'm a Person and I'm {Math.floor(Math.random() * 30)} years old!</p>
);
};
export default person;
We output random/dynamic content into our HTML.
I'm a Person and I'm 29 years old!
I'm a Person and I'm 2 years old!
I'm a Person and I'm 5 years old!
// src/App.js
import React, { Component } from 'react';
import './App.css';
import Person from './Person/Person';
class App extends Component {
render() {
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
<Person name="Max" age="29" />
<Person name="Jerôme" age="26" />
<Person name="Morgane" age="27" />
</div>
);
}
}
export default App;
// src/Person/Person.js
import React from 'react';
const person = (props) => {
return (
<p>
I'm a {props.name} and I'm {props.age} years old!
</p>
);
};
export default person;
Note: class based component, use this.props
.
// src/App.js
//...
<Person name="Jerôme" age="26">
My hobbies: Racing
</Person>
//...
We can access it via props.children
.
// src/Person/Person.js
import React from 'react';
const person = (props) => {
return (
<div>
<p>
I'm a {props.name} and I'm {props.age} years old!
</p>
<p>{props.children}</p>
</div>
);
};
export default person;
Note: changing the state, re-render the component.
// src/App.js
import React, { Component } from 'react';
import './App.css';
import Person from './Person/Person';
class App extends Component {
state = {
persons: [
{ name: 'Max', age: 29 },
{ name: 'Jerôme', age: 26 },
{ name: 'Morgane', age: 27 },
],
};
render() {
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
<button>Switch Name</button>
<Person
name={this.state.persons[0].name}
age={this.state.persons[0].age}
/>
<Person
name={this.state.persons[1].name}
age={this.state.persons[1].age}
>
My hobbies: Racing
</Person>
<Person
name={this.state.persons[2].name}
age={this.state.persons[2].age}
/>
</div>
);
}
}
export default App;
props
and state
are CORE concepts of React. Actually, only changes in props
and/ or state
trigger React to re-render your components and potentially update the DOM in the browser (a detailed look at how React checks whether to really touch the real DOM is provided in section 6).
Props
props
allow you to pass data from a parent (wrapping) component to a child (embedded) component.
State
Whilst props
allow you to pass data down the component tree (and hence trigger an UI update), state
is used to change the component, well, state from within. Changes to state also trigger an UI update.
Let's add onClick
on our button and link with a method switchNameHandler
.
// src/App.js
import React, { Component } from 'react';
import './App.css';
import Person from './Person/Person';
class App extends Component {
state = {
persons: [
{ name: 'Max', age: 29 },
{ name: 'Jerôme', age: 26 },
{ name: 'Morgane', age: 27 },
],
};
switchNameHandler = () => {
console.log('was clicked!');
};
render() {
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
<button onClick={this.switchNameHandler}>Switch Name</button>
<Person
name={this.state.persons[0].name}
age={this.state.persons[0].age}
/>
<Person />
</div>
);
}
}
export default App;
A list of supported events.
Event names | onCopy onCut onPaste |
---|---|
Properties | - DOMDataTransfer clipboardData |
Event names | onCompositionEnd onCompositionStart onCompositionUpdate |
---|---|
Properties | - string data |
Event names | onKeyDown onKeyPress onKeyUp |
---|---|
Properties | - boolean altKey |
- number charCode |
|
- boolean ctrlKey |
|
- boolean getModifierState(key) |
|
- string key |
|
- number keyCode |
|
- string locale |
|
- number location |
|
- boolean metaKey |
|
- boolean repeat |
|
- boolean shiftKey |
|
- number which |
Note: these focus events work on all elements in the React DOM, not just form elements.
Event names | onFocus onBlur |
---|---|
Properties | - DOMEventTarget relatedTarget |
Note: for more information about the onChange event, see Forms.
Event names | onChange onInput onInvalid onSubmit |
---|---|
Properties | – |
Note: The onMouseEnter
and onMouseLeave
events propagate from the element being left to the one being entered instead of ordinary bubbling and do not have a capture phase.
onClick
onContextMenu
onDoubleClick
onDrag
onDragEnd
onDragEnter
onDragExit
onDragLeave
onDragOver
onDragStart
onDrop
onMouseDown
onMouseEnter
onMouseLeave
onMouseMove
onMouseOut
onMouseOver
onMouseUp
Event names | ... (list above) |
---|---|
Properties | - boolean altKey |
- number button |
|
- number buttons |
|
- number clientX |
|
- number clientY |
|
- boolean ctrlKey |
|
- boolean getModifierState(key) |
|
- boolean metaKey |
|
- number pageX |
|
- number pageY |
|
- DOMEventTarget relatedTarget |
|
- number screenX |
|
- number screenY |
|
- boolean shiftKey |
Event names | onSelect |
---|---|
Properties | – |
Event names | onTouchCancel onTouchEnd onTouchMove onTouchStart |
---|---|
Properties | - boolean altKey |
- DOMTouchList changedTouches |
|
- boolean ctrlKey |
|
- boolean getModifierState(key) |
|
- boolean metaKey |
|
- boolean shiftKey |
|
- DOMTouchList targetTouches |
|
- DOMTouchList touches |
Event names | onScroll |
---|---|
Properties | - number detail |
- DOMAbstractView view |
Event names | onWheel |
---|---|
Properties | - number deltaMode |
- number deltaX |
|
- number deltaY |
|
- number deltaZ |
onAbort
onCanPlay
onCanPlayThrough
onDurationChange
onEmptied
onEncrypted
onEnded
onError
onLoadedData
onLoadedMetadata
onLoadStart
onPause
onPlay
onPlaying
onProgress
onRateChange
onSeeked
onSeeking
onStalled
onSuspend
onTimeUpdate
onVolumeChange
onWaiting
Event names | ... (list above) |
---|---|
Properties | – |
Event names | onLoad onError |
---|---|
Properties |
Event names | onAnimationStart onAnimationEnd onAnimationIteration |
---|---|
Properties | - string animationName |
- string pseudoElement |
|
- float elapsedTime |
Event names | onTransitionEnd |
---|---|
Properties | - string propertyName |
- string pseudoElement |
|
- float elapsedTime |
Event names | onToggle |
---|---|
Properties | – |
// src/App.js
//...
switchNameHandler = () => {
// console.log('was clicked!');
// DON'T DO THIS: this.state.persons[0].name = 'Maxime';
// WE SHOULDN'T MUTATE STATE DIRECTLY
this.setState({
persons: [
{ name: 'Maxime', age: 39 },
{ name: 'Jerôme', age: 36 },
{ name: 'Morgane', age: 37 },
],
});
};
//...
Note about using capital letter when we create a new component.
Since React 16.8, there also is a way to manage state in functional components with a feature called React Hooks.
A very important note here. setState
replaces the old state with the new state!
When you're using React hooks, your function which you get as the second element (commonly call setXXX
) in that array does not merge whatever you pass to it with the old state, instead it replaces the old state with it and this is super important because this means that whenever you're updating the state, you have to manually make sure you include all old state data.
// src/AppHooks.js
import React, { useState } from 'react';
import './App.css';
import Person from './Person/Person';
const AppHooks = (props) => {
const [personsState, setPersonsState] = useState({
persons: [
{ name: 'Maxime', age: 39 },
{ name: 'Jerôme', age: 36 },
{ name: 'Morgane', age: 37 },
],
otherState: 'some other value', // not recommended, but only for example purpose
});
const switchNameHandler = () => {
this.setState({
persons: [
{ name: 'Maxime', age: 39 },
{ name: 'Jerôme', age: 36 },
{ name: 'Morgane', age: 37 },
],
otherState: personsState.otherState,
});
};
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
<button onClick={switchNameHandler}>Switch Name</button>
<Person
name={personsState.persons[0].name}
age={personsState.persons[0].age}
/>
<Person
name={personsState.persons[1].name}
age={personsState.persons[1].age}
>
My hobbies: Racing
</Person>
<Person
name={personsState.persons[2].name}
age={personsState.persons[2].age}
/>
</div>
);
};
export default AppHooks;
A more elegant solution and also recommened by React team – use useState
multiple times.
//...
const [personsState, setPersonsState] = useState({
persons: [
{ name: 'Maxime', age: 39 },
{ name: 'Jerôme', age: 36 },
{ name: 'Morgane', age: 37 },
],
});
const [otherState, setOtherState] = useState('some other value');
//...
To summarize React Hooks is all about these use
something functions with useState
being the most important, that allow you to add functionality to functional components, like here useState
allows us to add state management to functional components.
A stateful component is a component that manages state, no matter if it's using the useState
Hook or a class-based approach with the state property.
A component like Person
int the person.js
file is a stateless component because it has no internal state management.
It is a good practice to create as many of these stateless components, also called dumb because they don't have any internal logic. Or presentational components because they present something, they output content, they only get external data and output it in a structured way.
The stateful components (either class-based with state or functional with useState) are also called smart components or container components because they contain the state of your application.
You should only get a few stateful components and a lot more stateless components. Why? Because this makes your app easier to maintain and manage. You have a clear flow of data and it's very clear where your main logic sits and where your data changes => then is distributed to the rest of your app.
The idea is to pass a reference of the method by props. You can pass methods also as props so that you can call a method which might change the state in another component which doesn't have direct access to the state and which shouldn't have direct access to the state.
//...
<Person
name={this.state.persons[1].name}
age={this.state.persons[1].age}
click={this.switchNameHandler}
>
My hobbies: Racing
</Person>
//...
import React from 'react';
const person = (props) => {
return (
<div>
<p onClick={props.click}>
I'm a {props.name} and I'm {props.age} years old!
</p>
<p>{props.children}</p>
</div>
);
};
export default person;
But how can we pass a parameter? Let's say we want to change the name.
this.switchNameHandler.bind(this, 'Maxime')
//...
<Person
name={this.state.persons[1].name}
age={this.state.persons[1].age}
click={this.switchNameHandler.bind(this, 'Maxime')}
>
My hobbies: Racing
</Person>
//...
() => this.switchNameHandler()
Earlier, we said that we shouldn't call this.switchNameHandler
with ()
and that was true but in this case this is not getting executed immediately. Instead what we pass here is an anonymous function which will be executed on a click and which then returns the result of this function getting executed. Which of course simply leads to this function getting executed.
Note: this can be inefficient in some case, bind is recommended.
//...
<Person
name={this.state.persons[1].name}
age={this.state.persons[1].age}
click={() => this.switchNameHandler('Maxime!!')}
>
My hobbies: Racing
</Person>
//...
Let's build a method nameChangedHandler
.
// src/App.js
import React, { Component } from 'react';
import './App.css';
import Person from './Person/Person';
class App extends Component {
state = {
persons: [
{ name: 'Max', age: 29 },
{ name: 'Jerôme', age: 26 },
{ name: 'Morgane', age: 27 },
],
otherState: 'some other value',
};
switchNameHandler = (newName) => {
this.setState({
persons: [
{ name: newName, age: 39 },
{ name: 'Jerôme', age: 36 },
{ name: 'Morgane', age: 37 },
],
});
};
nameChangedHandler = (event) => {
this.setState({
persons: [
{ name: 'Max', age: 29 },
{ name: event.target.value, age: 26 },
{ name: 'Morgane', age: 27 },
],
});
};
render() {
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
<button onClick={this.switchNameHandler.bind(this, 'Maxime1')}>
Switch Name
</button>
<Person
name={this.state.persons[0].name}
age={this.state.persons[0].age}
/>
<Person
name={this.state.persons[1].name}
age={this.state.persons[1].age}
click={this.switchNameHandler.bind(this, 'Maxime2')}
changed={this.nameChangedHandler}
>
My hobbies: Racing
</Person>
<Person
name={this.state.persons[2].name}
age={this.state.persons[2].age}
/>
</div>
);
}
}
export default App;
// src/Person.js
import React from 'react';
const person = (props) => {
return (
<div>
<p onClick={props.click}>
I'm a {props.name} and I'm {props.age} years old!
</p>
<p>{props.children}</p>
<input type="text" onChange={props.changed} value={props.name} />
</div>
);
};
export default person;
Right now, there are two ways of styling we can implement. The first one is to add a css file. Let's create Person.css
in the same folder than Person.js
!! It is not scoped to Person.js, it is global css.
Don't forget to import the css in your React file.
Note: very important detail, whichever css code we write in here is not scoped to this person.js
!!
.Person {
width: 60%;
margin: 10px auto;
border: 1px solid #eee;
box-shadow: 0 2px 3px #ccc;
padding: 16px;
text-align: center;
}
import React from 'react';
import './Person.css';
const person = (props) => {
return (
<div className="Person">
<Content />
</div>
);
};
export default person;
How does it work? Webpack will inject globally our css (we can see it from the Google Dev tool). It will automatically prefix it to work in as many browsers as possible.
// src/App.js
import React, { Component } from 'react';
import './App.css';
import Person from './Person/Person';
class App extends Component {
//...
render() {
const style = {
backgroundColor: 'white',
font: 'inherit',
border: '1px solid blue',
padding: '8px',
cursor: 'pointer',
};
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
<button style={style}>Switch Name</button>
<TheRest />
</div>
);
}
}
export default App;