React: Difference between revisions
Line 425: | Line 425: | ||
= The New Way Example using custom hooks = | = The New Way Example using custom hooks = | ||
This is the example from the react course where they broke the game into a custom hook | This is the example from the react course where they broke the game into a custom hook | ||
== Display Elements == | |||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
Line 458: | Line 460: | ||
); | ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Custom Hook == | |||
Here is the custom hook which abstracts the state and returns what is necessary for the display element of the game | Here is the custom hook which abstracts the state and returns what is necessary for the display element of the game | ||
Line 494: | Line 498: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== The Game == | |||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
Line 569: | Line 575: | ||
return <Game key={gameId} startNewGame={() => setGameId(gameId + 1)}/>; | return <Game key={gameId} startNewGame={() => setGameId(gameId + 1)}/>; | ||
} | } | ||
== Utilities == | |||
// Color Theme | // Color Theme |
Revision as of 02:27, 31 May 2020
Quick Reference
Variables
const myVar = (
<h1>Love it</h1>
);
Run
Maps
// Only do this if items have no stable IDs
const todo = ["fix", "syntax", "highlighting"];
const todoItems = todos.map((todo, index) =>
<li key={index}>
{todo.text}
</li>
);
Component Example
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {name: "Frarthur"};
}
render() {
return <div></div>;
}
};
Passing Props to child
Parent.json
import React from 'react';
import ReactDOM from 'react-dom';
import { Child } from './Child';
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { name: 'Frarthur' };
}
render() {
return <Child name={this.state.name} />;
}
}
ReactDOM.render(<Parent />, document.getElementById('app'));
Child.json
import React from 'react';
export class Child extends React.Component {
render() {
return <h1>Hey, my name is {this.props.name}!</h1>;
}
}
Forms with Ref
You can capture import using the React.createRef() e.g.
class Form extends React.Component
{
userNameInput = React.createRef();
handleSubmit = (event) => {
event.preventDefault()
console.log(userNameInput.current.value)
}
render()
{
return (
<form onSubmit={this.handleSubmit} >
<input
type="text"
placeHolder="background text"
ref={this.userNameInput}
required
/>
/>
)
}
}
Forms without Ref
A Better way may be to use a state to hold the name.
class Form extends React.Component
{
state = { userName : ''}
render()
{
return (
<form onSubmit={this.handleSubmit} >
<input
type="text"
value={this.state.userName}
OnChange = {
event => {
this.setState( {userName: event.target.value} )
}
}
required
/>
/>
)
}
}
Binding a function
import React from 'react';
import ReactDOM from 'react-dom';
import { Video } from './Video';
import { Menu } from './Menu';
const VIDEOS = {
fast: 'https://s3.amazonaws.com/codecademy-content/courses/React/react_video-fast.mp4',
slow: 'https://s3.amazonaws.com/codecademy-content/courses/React/react_video-slow.mp4',
cute: 'https://s3.amazonaws.com/codecademy-content/courses/React/react_video-cute.mp4',
eek: 'https://s3.amazonaws.com/codecademy-content/courses/React/react_video-eek.mp4'
};
class App extends React.Component {
constructor(props) {
this.chooseVideo = this.chooseVideo.bind(this);
super(props);
this.state = {
src: VIDEOS.fast
};
}
chooseView(newView) {
this.setState({
src: VIDEOS[newVideo]
});
}
render() {
return (
<div>
<h1>Video Player</h1>
<Menu chooseVideo={this.chooseView()} />
<Video src={this.state.src} />
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
import React from 'react';
export class Menu extends React.Component {
constructor() {
super(props)
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
var text = e.target.value;
this.props.chooseView(text);
}
render() {
return (
<form OnClick={this.handleClick}>
<input type="radio" name="src" value="fast" /> fast
<input type="radio" name="src" value="slow" /> slow
<input type="radio" name="src" value="cute" /> cute
<input type="radio" name="src" value="eek" /> eek
</form>
);
}
}
import React from 'react';
export class Video extends React.Component {
render() {
return (
<div>
<video controls autostart autoPlay muted src={this.props.src} />
</div>
);
}
}
Inline Styling
React needs double quotes to inject style in JSX e.g.
import React from 'react';
import ReactDOM from 'react-dom';
const styleMe = <h1 style={{ background: 'lightblue', color: 'darkred' }}>Please style me! I am so bland!</h1>;
ReactDOM.render(
styleMe,
document.getElementById('app')
);
Another example
mport React from 'react';
import ReactDOM from 'react-dom';
const styles = {
background: 'lightblue',
color: 'darkred',
marginTop: '100px',
fontSize: '50px'
}
const styleMe = <h1 style={styles}>Please style me! I am so bland!</h1>;
ReactDOM.render(
styleMe,
document.getElementById('app')
);
You can write px styles without quotes e.g.
const styles = {
background: 'lightblue',
color: 'darkred',
marginTop: 100,
fontSize: 50
}
Stateless Classes
A component class written in the usual way:
export class MyComponentClass extends React.Component {
render() {
return <h1>Hello world</h1>;
}
}
// The same component class, written as a stateless functional component:
export const MyComponentClass = () => {
return <h1>Hello world</h1>;
}
You can pass the properties from the container class like this
import React from 'react';
export const GuineaPigs = (props) => {
let src = props.src;
return (
<div>
<h1>Cute Guinea Pigs</h1>
<img src={src} />
</div>
);
}
Prototypes
Add them at the bottom so people can see how to use the code
import React from 'react';
export class BestSeller extends React.Component {
render() {
return (
<li>
Title: <span>
{this.props.title}
</span><br />
Author: <span>
{this.props.author}
</span><br />
Weeks: <span>
{this.props.weeksOnList}
</span>
</li>
);
}
}
// This propTypes object should have
// one property for each expected prop:
BestSeller.propTypes = {
title: React.PropTypes.string.isRequired,
author: React.PropTypes.string.isRequired,
weeksOnList: React.PropTypes.number.isRequired,
};
And a render example
import React from 'react';
import ReactDOM from 'react-dom';
import { BestSeller } from './BestSeller';
export class BookList extends React.Component {
render() {
return (
<div>
<h1>Best Sellers</h1>
<div>
<ol>
<BestSeller
title="Glory and War Stuff for Dads"
author="Sir Eldrich Van Hoorsgaard"
weeksOnList={10} />
<BestSeller
title="The Crime Criminals!"
author="Brenda Sqrentun"
weeksOnList={2} />
<BestSeller
title="Subprime Lending For Punk Rockers"
author="Malcolm McLaren"
weeksOnList={600} />
</ol>
</div>
</div>
);
}
}
ReactDOM.render(<BookList />, document.getElementById('app'));
And for stateless functions
import React from 'react';
export class GuineaPigs extends React.Component {
render() {
let src = this.props.src;
return (
<div>
<h1>Cute Guinea Pigs</h1>
<img src={src} />
</div>
);
}
}
GuineaPigs.propTypes = {
src: React.PropTypes.string.isRequired
};
Form Example where we track the input value
import React from 'react';
import ReactDOM from 'react-dom';
export class Input extends React.Component {
constructor(props) {
super(props);
this.state = { userInput: '' };
this.handleUserInput = this.handleUserInput.bind(this);
}
handleUserInput(e) {
this.setState({userInput: e.target.value});
}
render() {
return (
<div>
<input type="text" onChange={this.handleUserInput} value={this.state.userInput} />
<h1>{this.state.userInput}</h1>
</div>
);
}
}
ReactDOM.render(
<Input />,
document.getElementById('app')
);
The New Way Example using custom hooks
This is the example from the react course where they broke the game into a custom hook
Display Elements
const StarsDisplay = props => (
<>
{utils.range(1, props.count).map(starId => (
<div key={starId} className="star" />
))}
</>
);
const PlayNumber = props => (
<button
className="number"
style={{backgroundColor: colors[props.status]}}
onClick={() => props.onClick(props.number, props.status)}
>
{props.number}
</button>
);
const PlayAgain = props => (
<div className="game-done">
<div
className="message"
style={{ color: props.gameStatus === 'lost' ? 'red' : 'green'}}
>
{props.gameStatus === 'lost' ? 'Game Over' : 'Nice'}
</div>
<button onClick={props.onClick}>Play Again</button>
</div>
);
Custom Hook
Here is the custom hook which abstracts the state and returns what is necessary for the display element of the game
const useGameState = timeLimit => {
const [stars, setStars] = useState(utils.random(1, 9));
const [availableNums, setAvailableNums] = useState(utils.range(1, 9));
const [candidateNums, setCandidateNums] = useState([]);
const [secondsLeft, setSecondsLeft] = useState(10);
useEffect(() => {
if (secondsLeft > 0 && availableNums.length > 0) {
const timerId = setTimeout(() => setSecondsLeft(secondsLeft - 1), 1000);
return () => clearTimeout(timerId);
}
});
const setGameState = (newCandidateNums) => {
if (utils.sum(newCandidateNums) !== stars) {
setCandidateNums(newCandidateNums);
} else {
const newAvailableNums = availableNums.filter(
n => !newCandidateNums.includes(n)
);
setStars(utils.randomSumIn(newAvailableNums, 9));
setAvailableNums(newAvailableNums);
setCandidateNums([]);
}
};
return { stars, availableNums, candidateNums, secondsLeft, setGameState };
};
The Game
const Game = props => {
const {
stars,
availableNums,
candidateNums,
secondsLeft,
setGameState,
} = useGameState();
const candidatesAreWrong = utils.sum(candidateNums) > stars;
const gameStatus = availableNums.length === 0
? 'won'
: secondsLeft === 0 ? 'lost' : 'active'
const numberStatus = number => {
if (!availableNums.includes(number)) {
return 'used';
}
if (candidateNums.includes(number)) {
return candidatesAreWrong ? 'wrong' : 'candidate';
}
return 'available';
};
const onNumberClick = (number, currentStatus) => {
if (currentStatus === 'used' || secondsLeft === 0) {
return;
}
const newCandidateNums =
currentStatus === 'available'
? candidateNums.concat(number)
: candidateNums.filter(cn => cn !== number);
setGameState(newCandidateNums);
};
return (
<div className="game">
<div className="help">
Pick 1 or more numbers that sum to the number of stars
</div>
<div className="body">
<div className="left">
{gameStatus !== 'active' ? (
<PlayAgain onClick={props.startNewGame} gameStatus={gameStatus} />
) : (
<StarsDisplay count={stars} />
)}
</div>
<div className="right">
{utils.range(1, 9).map(number => (
<PlayNumber
key={number}
status={numberStatus(number)}
number={number}
onClick={onNumberClick}
/>
))}
</div>
</div>
<div className="timer">Time Remaining: {secondsLeft}</div>
</div>
);
};
const StarMatch = () => {
const [gameId, setGameId] = useState(1);
return <Game key={gameId} startNewGame={() => setGameId(gameId + 1)}/>;
}
== Utilities ==
// Color Theme
const colors = {
available: 'lightgray',
used: 'lightgreen',
wrong: 'lightcoral',
candidate: 'deepskyblue',
};
// Math science
const utils = {
// Sum an array
sum: arr => arr.reduce((acc, curr) => acc + curr, 0),
// create an array of numbers between min and max (edges included)
range: (min, max) => Array.from({length: max - min + 1}, (_, i) => min + i),
// pick a random number between min and max (edges included)
random: (min, max) => min + Math.floor(Math.random() * (max - min + 1)),
// Given an array of numbers and a max...
// Pick a random sum (< max) from the set of all available sums in arr
randomSumIn: (arr, max) => {
const sets = [[]];
const sums = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 0, len = sets.length; j < len; j++) {
const candidateSet = sets[j].concat(arr[i]);
const candidateSum = utils.sum(candidateSet);
if (candidateSum <= max) {
sets.push(candidateSet);
sums.push(candidateSum);
}
}
}
return sums[utils.random(0, sums.length - 1)];
},
};
ReactDOM.render(<StarMatch />, mountNode);
Type Script
This is another way to valid props
import * as React from "react";
interface Props {
name: string
}
function Greeting(props : Props) {
return (
<h1>Hello {props.name}</h1>
)
}
Lifecycle
A component “mounts” when it renders for the first time. This is when mounting lifecycle methods get called.
There are three mounting lifecycle methods:
- componentWillMount
- render
- componentDidMount
There are five updating lifecycle methods:
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
Some words of wisdom
A React component should use props to store information that can be changed, but can only be changed by a different component.
A React component should use state to store information that the component itself can change.