React
start
npx create-react-app <name>
JSX
Use in code. Place expressions inside {}.
const name = 'Josh Perez';
// JSX
const element = (
<h1 className='test'>
{/* JSX comments */}
Hello, {name}.
</h1>
);
// equals to
const element = React.createElement(
'h1',
{className: 'test'},
'Hello, ' + name + '.'
);
-
extend Attributes
function App1() { return <Greeting firstName="Ben" lastName="Hector" />; } function App2() { const props = {firstName: 'Ben', lastName: 'Hector'}; return <Greeting {...props} />; }
-
Some equal expressions
<MyComponent message="hello world" /> <MyComponent message={'hello world'} /> // expression returns the string <MyComponent message="<3" /> <MyComponent message={'<3'} /> <MyTextBox autocomplete /> // default = true <MyTextBox autocomplete={true} />
Render
// React Elements are constant.
const element = <h1> Hello World! </h1>;
ReactDOM.render(element, document.getElementById('root'));
// Update
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
Component & Prop[ertie]s
// Function
function welcome(props){
return <h1>Hello, {props.name}</h1>;
}
// Class (ES6)
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
-
Create Element through Component.
const element = <Welcome name='Sara' />; // this.props.name = 'Sara'
Class Component name Must start with a Capital Letter.
<div/>
is a HTML tag, while<Div />
may be a component.
-
Compose Components
function Welcome(props) { return <h1>Hello, {props.name}</h1>; } function App() { return ( <div> <Welcome name="Sara" /> <Welcome name="Cahal" /> <Welcome name="Edite" /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') );
- Props are Read-Only
States & Lifecycle Methods
We use class component to implement dynamic states.
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
// Mount, run on render
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
// Unmount, run on destruct
componentWillUnmount() {
clearInterval(this.timerID);
}
// setState: Core of dynamics.
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Only use setState()
to update state.
-
setState()
is Asynchronous!incrementCount() { // 注意:这样 *不会* 像预期的那样工作。 this.setState({count: this.state.count + 1}); } handleSomething() { // 假设 `this.state.count` 从 0 开始。 this.incrementCount(); this.incrementCount(); this.incrementCount(); // 当 React 重新渲染该组件时,`this.state.count` 会变为 1,而不是你期望的 3。 // 这是因为上面的 `incrementCount()` 函数是从 `this.state.count` 中读取数据的, // 但是 React 不会更新 `this.state.count`,直到该组件被重新渲染。 // 所以最终 `incrementCount()` 每次读取 `this.state.count` 的值都是 0,并将它设为 1。 // 问题的修复参见下面的说明。 }
Instead, pass in a function to
setState()
solves this problem.incrementCount() { this.setState((state) => {return {count: state.count + 1}}); // 重要:在更新的时候读取 `state`,而不是 `this.state`。 } handleSomething() { // 假设 `this.state.count` 从 0 开始。 this.incrementCount(); this.incrementCount(); this.incrementCount(); // 如果你现在在这里读取 `this.state.count`,它还是会为 0。 // 但是,当 React 重新渲染该组件时,它会变为 3。 }
Event
-
Default Behavior
eg. click
<a>
will navigate tohref
.eg. click a
submit
input in a form will submit to server.eg. click and drag on
textarea
will select.How to prevent:
event.preventDefault();
-
<button onClick={func}>
class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; this.handleClick = this.handleClick.bind(this); // necessary } handleClick(event) { this.setState(state => ({ isToggleOn: !state.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } } ReactDOM.render( <Toggle />, document.getElementById('root') );
-
event
event.type // "click" event.target //
Conditional Rendering
-
Logic Expressions
return ( <div> {warn === true && <p> Warning! </p>} </div> );
-
Return null to avoid rendering.
function WarningBanner(props) { if (!props.warn) { return null; } return ( <div className="warning"> Warning! </div> ); }
List & Key
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}> // should provide a unique key.
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Dynamic Rendering
function DynamicTable() {
const elements = ['one', 'two', 'three'];
return (
<ul>
{elements.map((value, index) => {
return <li key={index}>{value}</li>
})}
</ul>
)
}
function DynamicForm(){
const [inputFields, setInputFields] = useState([{ firstName: '', lastName: '' }]);
const handleAddFields = () => {
const values = [...inputFields];
values.push({ firstName: '', lastName: '' });
setInputFields(values);
};
const handleRemoveFields = index => {
const values = [...inputFields];
values.splice(index, 1);
setInputFields(values);
};
return (
<>
<form onSubmit={handleSubmit}>
<div className="form-row">
{inputFields.map((inputField, index) => (
<Fragment key={index}>
<div className="form-group col-sm-6">
<label htmlFor="firstName">First Name</label>
<input type="text" id="firstName" name="firstName" value={inputField.firstName} />
</div>
<div className="form-group col-sm-4">
<label htmlFor="lastName">Last Name</label>
<input type="text" id="lastName" name="lastName" value={inputField.lastName} />
</div>
<div className="form-group col-sm-2">
<button className="btn btn-link" type="button"> - </button>
<button className="btn btn-link" type="button"> + </button>
</div>
</Fragment>
))}
</div>
<div className="submit-button">
<button className="btn btn-primary mr-2" type="submit" onSubmit={handleSubmit}>
Save
</button>
</div>
</form>
</>
)
}
Refs
To access Child node in Parent node.
-
Create
class MyComponent extends React.Component { constructor(props) { super(props); this.myRef = React.createRef(); } render() { return <div ref={this.myRef} />; } }
-
Access
const node = this.myRef.current;
-
Usage
We cannot use Ref on Functional Components, since they have no instance.
class CustomTextInput extends React.Component { constructor(props) { super(props); // 创建一个 ref 来存储 textInput 的 DOM 元素 this.textInput = React.createRef(); this.focusTextInput = this.focusTextInput.bind(this); } focusTextInput() { // 直接使用原生 API 使 text 输入框获得焦点 // 注意:我们通过 "current" 来访问 DOM 节点 this.textInput.current.focus(); } render() { // 告诉 React 我们想把 <input> ref 关联到 // 构造器里创建的 `textInput` 上 return ( <div> <input type="text" ref={this.textInput} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); } }
Context
provide Global props
(themes, login-status) used by many components.
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext; // either use it here.
render() {
return <Button theme={this.context} />;
}
}
//ThemedButton.contextType = ThemeContext; // or use it here.
const myContext = React.createContext(defaultValue);
-
<myContext.Provider value={}> ... </myContext.Provider>
Components inside
will find the nearest context.
-
<myContext.Consumer> {value => ()} </myContext.Consumer>
For Functional Components without
this.context
, use this to getvalue
.
// Multiple Context
// Theme context,默认的 theme 是 “light” 值
const ThemeContext = React.createContext('light');
// 用户登录 context
const UserContext = React.createContext({
name: 'Guest',
});
class App extends React.Component {
render() {
const {signedInUser, theme} = this.props;
// 提供初始 context 值的 App 组件
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
function Layout() {
return (
<div>
<Sidebar />
<Content />
</div>
);
}
// 一个组件可能会消费多个 context
function Content() {
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
Render Props
pass in a render
attributes to dynamically render component inside component.
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
/*
使用 `render`prop 动态决定要渲染的内容,
而不是给出一个 <Mouse> 渲染结果的静态表示
*/
{this.props.render(this.state)}
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>移动鼠标!</h1>
<Mouse render={mouse => (<Cat mouse={mouse} />)}/> // render prop
</div>
);
}
}
Hook
It allows functional components to use states
. (Avoid annoying this
in class components!)
Cannot be used in class components.
-
useState(state)
import React, { useState } from 'react'; function Example() { // 声明一个叫 “count” 的 state 变量。 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } // async timer example function AsyncExample() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
-
useEffect(func, [condition])
import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } // replace componentDidMount & componentDidUpdate: ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // replace componentWillUnmount: return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; }
this equals to:
class FriendStatus extends React.Component { constructor(props) { super(props); this.state = { isOnline: null }; this.handleStatusChange = this.handleStatusChange.bind(this); } componentDidMount() { ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); } handleStatusChange(status) { this.setState({ isOnline: status.isOnline }); } render() { if (this.state.isOnline === null) { return 'Loading...'; } return this.state.isOnline ? 'Online' : 'Offline'; } }
Conditional Effect:
useEffect( () => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [props.source], // called only if props.source changed. ); useEffect( () => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [], // Never Update. );
-
useContext(myContext)
Also need
. const themes = { light: { foreground: "#000000", background: "#eeeeee" }, dark: { foreground: "#ffffff", background: "#222222" } }; const ThemeContext = React.createContext(themes.light); function App() { return ( <ThemeContext.Provider value={themes.dark}> <Toolbar /> </ThemeContext.Provider> ); } function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } function ThemedButton() { const theme = useContext(ThemeContext); // replace <Consumer> or this.context return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </button> ); }
Fragments
class Table extends React.Component {
render() {
return (
<table>
<tr>
<Columns />
</tr>
</table>
);
}
}
class Columns extends React.Component {
render() {
// wrong! <div> ruins <table>
return (
<div>
<td>Hello</td>
<td>World</td>
</div>
);
}
}
Instead, use:
class Columns extends React.Component {
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}
}
// or simply:
class Columns extends React.Component {
render() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
}
Other API
-
React.PureComponent
It has
shouldComponentUpdate()
method, allows shallow comparison to decide time to update.It improves performance only when
props & states
are simple.
-
React.memo
function component wrapper to improve performance.
Similar to
PureComponent
.
-
React.createElement(type[, props, children])
JSX will be converted to this function by Babel.
-
React.cloneElement(element[, props, children])
Clone an element with props shallow-merged.