Names:
React / React.js or React JS
A JavaScript library for building user interfaces.
React JS !== React Native
Initial release: May 2013
Created by Jordan Walke
at Facebook.
Current state: 16.8.6 (March 27, 2019)
Used by:
Hello-World example:
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('app')
);
JavaScript XML / JavaScript Syntax Extension
Extension of the common JavaScript grammar for React
JSX is not valid JavaScript so it needs to be compiled to be browser conformant
Example:
const heading = <h1>This is JSX</h1>;
In React the render logic is coupled with UI logic so it explains:
⇒ loosely coupled units called "components" *
Example:
const Nav = () => (
<ul id="nav">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
</ul>
);
*Components will be explained later
JSX needs to be compiled to JavaScript to be executable by a browser
Example:
const Nav = () => React.createElement("ul", {
id: "nav"
}, React.createElement("li", null, React.createElement("a", {
href: "#"
}, "item1")), React.createElement("li", null, React.createElement("a", {
href: "#"
}, "item2")));
const Nav = () => (
<ul id="nav">
<li><a href="#">item1</a></li>
<li><a href="#">item2</a></li>
</ul>
);
Will be compiled to:
If you don't want to use JSX give this projects a try:
Render a variable using curly braces {}
const name = 'Axel Forstenhäusler';
const element = <h1>Hello, {name}!</h1>;
ReactDOM.render(
element,
document.getElementById('app')
);
// Expected output: Hello, Axel Forstenhäusler!
all valid JavaScript expressions are available
(+,-,*, ...)
const formatName = ({ firstname, lastname }) => `${firstname} ${lastname}`;
const user = {
firstName: 'Axel',
lastName: 'Forstenhäusler'
};
const element = (
<h1>Hello, {formatName(user)}!</h1>
);
ReactDOM.render(
element,
document.getElementById('root')
);
// Expected output: Hello, Axel Forstenhäusler!
Since JSX is basically JavaScript you can use all methods, statements and operators like .map, if, ternary, etc.
const getGreeting = (user) => {
if (user) return <h1>Hello, {formatName(user)}!</h1>;
return <h1>Hello, Stranger.</h1>;
}
Use quotes to specify string literals as attributes:
const button = <button tabIndex="0">Click!</button>;
Use curly braces to embed JavaScript expressions in an attribute/prop
const image = <img src={avatarUrl} alt="avatar" />;
React DOM: camelCase property naming convention instead of HTML attribute names
For example:
class becomes className
tabindex becomes tabIndex
In JSX you just loop over the array and return the JSX you want to render:
const persons = [
'Axel',
'Christophe',
'Immanuel',
'Sebastian',
];
const App = () => persons.map(person => <p>{person}</p>);
// Expected result:
// Axel
// Christophe
// Immanuel
// Sebastian
Like other frameworks React is based on components.
Therefore the application consists of logically separated and reusable units.
There are two different types of components in React:
functional components
class components
Functional components:
const Welcome = ({ name }) => <h1>Hello, {name}!</h1>;
These are called "functional component" because they are literal JavaScript functions.
They also cannot have state or access
class Welcome extends React.Component {
render() {
const { name } = this.props;
return <h1>Hello, {name}</h1>;
}
}
Class components:
These may have state and may access component lifecycle hooks as well.
State repressents a component's data. A component is able to modify its state by itself. State may also be passed to child components using props.
class App extends React.Component {
state = {
message: 'Hello I am a state message!',
};
handleClick = () => {
const { message } = this.state;
const newMessage = message.split('').reverse().join('');
this.setState({ message: newMessage });
}
render() {
const { message } = this.state;
return (
<div>
<h1>{message}</h1>
<button onClick={this.handleClick}>Reverse message</button>
</div>
);
}
}
You may modify a component's state using events and corresponding handler functions.
class App extends React.Component {
state = {
message: 'Hello I am a state message!',
value: '',
};
handleClick = () => {
const { message } = this.state;
this.setState({ message: message.split('').reverse().join('') });
}
handleChange = (e) => {
this.setState({ value: e.currentTarget.value });
}
render() {
const { message, value } = this.state;
return (
<div>
<p>{message}</p>
<button onClick={this.handleClick}>Reverse message</button>
<p>{value}</p>
<input type="text" value={value} onChange={this.handleChange} />
</div>
);
}
}
Things to remember about state in React:
Do not modify state directly (make local copies and use setState):
handleClick = () => {
const { data } = this.state;
data = `I modify ${data} now.`;
this.setState({ data });
}
Remember that setState is async!
"Props" stands for properties.
Using props we may pass data from a parent to a child component.
const Parent = () => <Child content="I got passed by the parent"/>;
const Child = ({ content }) => (
<>
<h3>I render the passed data:</h3>
<p>{content}!</p>
</>
);
// Expected result:
// I render the passed data:
// I got passed by the parent!
The child component may not modify the prop data. To do that it has to emit an event to the parent which will modify it according to its business logic.
Components can refer to other components
Example: an App component which renders several Welcome components
const Welcome = ({ name }) => <h1>Hello, {name}!</h1>;
const App = () => (
<>
<Welcome name="Axel" />
<Welcome name="Christophe" />
<Welcome name="Immanuel" />
</>
);
// Expected output:
// Hello, Axel!
// Hello, Christophe!
// Hello, Immanuel!
There are multiple ways to render JSX conditionally:
const IfExample = () => {
if (trueStatement) return <h1>True case</h1>;
return <h1>False case</h1>;
}
const InlineExample = () => trueStatement ? <h1>True case</h1> : <h1>False case</h1>;
const OnlyTrue = () => (
<>
{unreadMessages.length > 0 &&
<h2>You have {unreadMessages.length} unread messages.</h2>
}
</>
);
Usually React applications have an App component as their entry point.
Splitting business logic into small reusable components is recommended.
To enable routing in React you need to wrap your app between one of five router components:
import { BrowserRouter, HashRouter, MemoryRouter, Router, StaticRouter } from 'react-router';
BrowserRouter: Uses HTML5 history API (/newsfeed)
HashRouter: Uses a hash portion of the URL (#/newsfeed)
MemoryRouter: Keeps URL in memory (useful for Native)
Router: Low-level interface for other router components
StaticRouter: Never changes its location (server side rendering)
The Route component defines which component should be rendered matching the URL.
Route parameters can be accessed through a prop.
import { Route, Redirect, Link } from 'react-router-dom';
<Route path="/news" component={News} />
<Route path="/users" exact component={ProfileList} />
<Route path="/users/:id" component={SingleProfile} />
A Link component defines a link which leads to a route.
import { Route, Redirect, Link } from 'react-router-dom';
<Link to="/news">Navigate to newsfeed</Link>
A Redirect component is used to lead the user somewhere else.
For example after a successful login redirect the user to the newsfeed
import { Route, Redirect, Link } from 'react-router-dom';
<Redirect to="/news" />
To use GraphQL together with React we will use the React Apollo Client.
What we need:
Apollo Boost: helps setting up an Apollo Client
React Apollo: View layer intergration of Apollo Client for React
GraphQL Tag: Parsing the GraphQL queries
Create a client:
import ApolloClient from 'apollo-boost';
import { gql } from 'graphql-tag';
import { ApolloProvider } from 'react-apollo';
const App = () => (
<ApolloProvider client={client}>
... Your app here
</ApolloProvider>
);
const client = new ApolloClient({
uri: '<your-url>',
});
Connect yout client to React:
To access Apollo's features you need to wrap your app into the ApolloProvider:
Now we are ready to use!
Create a client:
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from 'react-apollo';
const App = () => (
<ApolloProvider client={client}>
... Your app here
</ApolloProvider>
);
const client = new ApolloClient({
uri: '<your-url>',
});
Connect yout client to React:
To access Apollo's features you need to wrap your app into the ApolloProvider:
To request data we use the Query component of react-apollo:
const GET_PLANTS = gql`
{
plants {
name
type
}
}
`;
import gql from 'graphql-tag';
import { Query } from 'react-apollo';
After that we write our query using graphql-tag:
Pass the query to the Query component and render the response data:
<Query query={GET_PLANTS} fetchPolicy="network-only">
{({ loading, error, data }) => {
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return data.plants.map(({ name, type }) => (
<div key={name}>
<p>Name: {name}</p>
<p>Type: {type}</p>
</div>
));
}}
</Query>
const GET_PLANT = gql`
query Plant($name: String!) {
plants(where: { name: $name }) {
type
size
}
}
`;
To request specific data we need to define the query differently. GraphQL also checks whether the passed variable is of the correct type (in this case String).
Using the Query component we can pass a variables object:
<Query query={GET_PLANT} variables={{ name: 'ivy' }} fetchPolicy="network-only">
{({ loading, error, data }) => {
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
const [{ type, size }] = data.plants;
return (
<>
<p>Type: {type}</p>
<p>Size: {size}</p>
</>
)
}}
</Query>
For all kinds of mutations Apollo provides a Mutation component:
const CREATE_PLANT = gql`
mutation CreatePlant(
$name: String!
$type: String!
$size: Int!
) {
createPlant(data: {
name: $name
type: $type
size: $size
}) {
name
}
}
`;
import gql from 'graphql-tag';
import { Mutation } from 'react-apollo';
We define the mutation using graphql-tag again:
<Mutation mutation={ADD_TODO} ignoreResults>
{createPlant => (
<form
onSubmit={e => {
e.preventDefault();
createPlant({ variables: { name: name.value, type: type.value, size: size.value } });
}}
>
<input ref={node => name = node} type="text" placeholder="Name"/>
<input ref={node => type = node} type="text" placeholder="Type"/>
<input ref={node => size = node} type="number" placeholder="Size"/>
<button type="submit">Add plant</button>
</form>
)}
</Mutation>
Now we can pass the GraphQL statement to the Mutation component. We mutate data using a form and the React onSubmit handler.
We get the values from the inputs and pass them to the GraphQL statement: