What-Is-New-In-React-16
# What's new in React 16
# 1. 新的渲染返回值类型
React 16.2 is now available! The biggest addition is improved support for returning multiple children from a component’s render method. We call this feature fragments.[1]
# 1.1 数组结构
render() {
return [
<li key="A">A</li>
<li key="B">B</li>
]
}
2
3
4
5
6
# 1.2 Fragment类型
React v16.2.0提供了Fragment类型,不同于div等基本类型的区别在于,Fragment只是一个虚拟占用,并不会真正生成Dom节点
render() {
return <Fragment>
<span>Hello World 1</span>
<span>Hello World 2</span>
</Fragment>
}
2
3
4
5
6
或者
render() {
return <>
<span>Hello World 1</span>
<span>Hello World 2</span>
</>
}
2
3
4
5
6
# 1.3 含有Key标签的Fragment
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
// Without the `key`, React will fire a key warning
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
))}
</dl>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
# 2. 异步渲染
# 2.1 getDerivedStateFromProps
Replace all componentWillReceiveProps
uses and could return null (indicate not to update state) or return state.
Common Problems: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
// Before
class ExampleComponent extends React.Component {
state = {
isScrollingDown: false,
};
componentWillReceiveProps(nextProps) {
if (this.props.currentRow !== nextProps.currentRow) {
this.setState({
isScrollingDown:
nextProps.currentRow > this.props.currentRow,
});
}
}
}
// After
class ExampleComponent extends React.Component {
// Initialize state in constructor,
// Or with a property initializer.
state = {
isScrollingDown: false,
lastRow: null,
};
static getDerivedStateFromProps(props, state) {
if (props.currentRow !== state.lastRow) {
return {
isScrollingDown: props.currentRow > state.lastRow,
lastRow: props.currentRow,
};
}
// Return null to indicate no change to state.
return null;
}
}
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
# 2.2 getSnapshotBeforeUpdate
The new
getSnapshotBeforeUpdate
lifecycle is called right before mutations are made (e.g. before the DOM is updated). The return value for this lifecycle will be passed as the third parameter tocomponentDidUpdate
.
# 2.3 组合getSnapshotBeforeUpdate
、componentDidUpdate
class ScrollingList extends React.Component {
listRef = null;
getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the scroll position so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
return (
this.listRef.scrollHeight - this.listRef.scrollTop
);
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
// (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (snapshot !== null) {
this.listRef.scrollTop =
this.listRef.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.setListRef}>
{/* ...contents... */}
</div>
);
}
setListRef = ref => {
this.listRef = ref;
};
}
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
# 3. Context API v16.3.0
# 3.1 Before Use Context
Context is primarily used when some data needs to be accessible by many components at different nesting levels. Apply it sparingly because it makes component reuse more difficult.[4] Please try to use
Component Composition
[5] mode
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Note:
At Facebook, we use React in thousands of components, and we haven’t found any use cases where we would recommend creating component inheritance hierarchies. Props and composition give you all the flexibility you need to customize a component’s look and behavior in an explicit and safe way. Remember that components may accept arbitrary props, including primitive values, React elements, or functions. If you want to reuse non-UI functionality between components, we suggest extracting it into a separate JavaScript module. The components may import it and use that function, object, or a class, without extending it.
# 3.2 API
I would recommend reaching for Context when you find yourself passing props down through three or more levels in your component tree. You might notice that you have renamed your props, making it challenging to determine the data’s origin. You might consider implementing context if a bunch of your components know about irrelevant data.
Step 1: create context api
const MyContext = React.createContext(defaultValue);
Step 2: define provider
<MyContext.Provider value={/* some value */}>
Step 3: use as consumer
<MyContext.Consumer>
{value => /* render something based on the context value */}
</MyContext.Consumer>
2
3
# 3.3 forwardRef API[6]
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
2
3
4
5
6
7
8
9
# 3.4 StrictMode Component
StrictMode currently helps with:
- Identifying components with unsafe lifecycles
- Warning about legacy string ref API usage
- Warning about deprecated findDOMNode usage
- Detecting unexpected side effects
- Detecting legacy context API
Note: Strict mode checks are run in development mode only; they do not impact the production build.
# 4. Pointer Events
Only work in browsers that support the Point Event. The following event types are now available in React DOM:
- onPointerDown
- onPointerMove
- onPointerUp
- onPointerCancel
- onGotPointerCapture
- onLostPointerCapture
- onPointerEnter
- onPointerLeave
- onPointerOver
- onPointerOut
# 5. Lazy, Memo v16.6.0
[7]
Lazy and Memo is used for Funcional Component
and before this you should know what it is.
# 5.1 Functional Component Vs. Class Component
// Functional Components
function Hello(props){
return <div>Hello {props.name}</div>
}
2
3
4
Benefits:
- Easy to read with less code
- Easy to test with less stub library
- Better performance improve on package bundle size and mounting performance[9]
- Reduce coupling with less code in function
# 5.2 Functional Component Performance
We thought this change would instantly improve performance, because when navigating between conversations in Missive, React unmounts and mounts hundreds of components. You’d think functional components would avoid this mounting / unmounting as well as lifecycle events overhead because they are just functions, but you’d be wrong.
Although it is small improvement of performance, but still useful[10]. Demo Gif (opens new window)
class Main extends React.Component {
render() {
var dots = Array(500).fill(0).map(x => {
if(this.props.kind == 'stateless-functional-direct-call') {
return Dot();
} else if(this.props.kind == 'stateless-functional-mounted') {
return React.createElement(Dot, {}, {});
} else if(this.props.kind == 'stateful') {
return React.createElement(DotComponent, {}, {});
}
})
return React.createElement('div', {}, ...dots);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 5.3 How to use React Memo
memo just give functional component pure render ability.
// Demo
import React from "react";
class Index extends React.Component {
state = {
count: 0
};
handleClick = () => {
this.setState({
count: ++this.state.count
});
};
render() {
return <div>
<button onClick={this.handleClick}> {"Click Here to Refresh Component"}</button>
<br/>
<br/>
<br/>
<ClassComponent/>
<FunctionalComponent/>
<PureComponent/>
<MemoFunctionalComponent />
</div>;
}
}
export default Index;
function FunctionalComponent(props) {
console.log("render ----> FunctionalComponent");
return <div>{"FunctionalComponent"}</div>;
}
const MemoFunctionalComponent = React.memo((props) => {
console.log("render ----> MemoFunctionalComponent");
return <div>{"MemoFunctionalComponent"}</div>;
});
class ClassComponent extends React.Component {
render() {
console.log("render ----> ClassComponent");
return <div>{"ClassComponent"}</div>;
}
}
class PureComponent extends React.PureComponent {
render() {
console.log("render ----> PureComponent");
return <div>{"PureComponent"}</div>;
}
}
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
45
46
47
48
49
50
51
52
53
54
# 5.4 How to use React Lazy & Code Split
lazy is used for tow purpose: One is lazy loading component and show loading state. Another purpose is good for code split by model bundle tool, like webpack to reduce running package size
import React, { lazy, Suspense } from "react";
const LazyComponent = lazy(() => import("./lazy-component"));
export default class Index extends React.Component {
state = {
showLazyComponent: false
};
handleShowLazyComponent = () => this.setState({
showLazyComponent: !this.state.showLazyComponent
});
render() {
return <div>
<button onClick={this.handleShowLazyComponent}>Click Me to Show Lazy Component</button>
<br/>
<br/>
<br/>
<Suspense fallback={<div>Loading...</div>}>
{this.state.showLazyComponent && <LazyComponent/>}
</Suspense>
</div>;
}
}
// Lazy Component
import React from "react";
const LazyComponent = () => {
return <div>
{"This is LazyComponent for 10000 items"}
{new Array(10000).fill(null).map((_, index) => {
return <div key={index}>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
<div>{index}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>;
})}
</div>;
};
export default LazyComponent;
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# 6. React Hooks v16.8.0
Hooks let you use state and other React features without writing a class. You can also build your own Hooks to share reusable stateful logic between components.
Note that React Hooks don’t cover all use cases for classes yet but they’re very close. Currently, only
getSnapshotBeforeUpdate()
andcomponentDidCatch()
methods don’t have equivalent Hooks APIs, and these lifecycles are relatively uncommon. If you want, you should be able to use Hooks in most of the new code you’re writing. more examples to see (opens new window)[11]
Hooks:
- useState
- useEffect
- useContext
- useReducer
- useRef
- useLayoutEffect
- useDebugValue
- use customize hook
# 6.1 useState
Given functional component ability to maintains state
in itself.
function App() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
)
}
2
3
4
5
6
7
8
9
10
11
12
# 6.2 useEffect
useEffect
called after every render, just like componentDidMount
, componentDidUpdate
or componentWillUnmount
More info about customize useEffect (opens new window)
import React, { useEffect, useState } from "react";
export default function() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 6.3 useContext
More Reading - Redux vs. React Context API (opens new window)
import React, { useContext, useState } from "react";
const NumberContext = React.createContext(100);
export default function() {
const value = useContext(NumberContext);
const [count, setCount] = useState(value);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 6.4 useReducer
import React, { useReducer } from "react";
export default function() {
const [count, dispatch] = useReducer((state, action) => {
return state + action;
}, 0);
return (
<div>
<p>You Clicked {count} times</p>
<button onClick={() => dispatch(1)}>Click Me</button>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 6.5 useRef
import React, { useRef } from "react";
export default function() {
const inputEl = useRef(null);
return (
<div style={{ display: 'flex', flexFlow: 'column'}}>
<input ref={inputEl} type='text' />
<button onClick={() =>inputEl.current.focus()}>Click Me to Focus input</button>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
# 6.6 useLayoutEffect
https://reactjs.org/docs/hooks-reference.html#uselayouteffect
# 6.7 useDebugValue
https://reactjs.org/docs/hooks-reference.html#uselayouteffect
# 6.8 customize hook
More Examples (opens new window)
import { useState, useRef, useEffect, useCallback } from 'react';
// Usage
export default function (){
// State for storing mouse coordinates
const [coords, setCoords] = useState({ x: 0, y: 0 });
// Event handler utilizing useCallback ...
// ... so that reference never changes.
const handler = useCallback(
({ clientX, clientY }) => {
// Update coordinates
setCoords({ x: clientX, y: clientY });
},
[setCoords]
);
// Add event listener using our hook
useEventListener('mousemove', handler);
return (
<h1>
The mouse position is ({coords.x}, {coords.y})
</h1>
);
}
function useEventListener(eventName, handler, element = window){
// Create a ref that stores handler
const savedHandler = useRef();
// Update ref.current value if handler changes.
// This allows our effect below to always get latest handler ...
// ... without us needing to pass it in effect deps array ...
// ... and potentially cause effect to re-run every render.
useEffect(() => {
savedHandler.current = handler;
}, [handler]);
useEffect(
() => {
// Make sure element supports addEventListener
// On
const isSupported = element && element.addEventListener;
if (!isSupported) return;
// Create event listener that calls handler function stored in ref
const eventListener = event => savedHandler.current(event);
// Add event listener
element.addEventListener(eventName, eventListener);
// Remove event listener on cleanup
return () => {
element.removeEventListener(eventName, eventListener);
};
},
[eventName, element] // Re-run if eventName or element changes
);
};
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# React 16.9
# How to Use React.Profiler
Profiler对程序运行时的性能监控,React自身提供了性能监控工具,其可以独立运行,也可以在浏览器中加载第三方应用。
首先,本机全局安装yarn global add react-devtools
,安装成功后运行react-devtools
其次,在项目的index.html中添加<script src="http://localhost:8097"></script>
标签
最后,启动项目即可,yarn start
参考文章:https://kentcdodds.com/blog/profile-a-react-app-for-performance/
# React 16.10 ~ 16.13
fix issues
# React 16.14
# Add Support for New JSX Transform
JSX需要Babel转译城JS,React官方提供了新一代的转译基础语法支持,用户可在无需更改或者升级旧代码的基础上进行升级,需要开启新转译方式的选项即可。 步骤如下:
- Upgrade Babel
yarn upgrade @babel/core @babel/plugin-transform-react-jsx
Or Upgrade Babel Presentyarn upgrade @babel/core @babel/preset-react
- Upgrade React to 17.0.0
新的转译方式带来几个好处:
- 不需要在'头部'声明React的导入语句
- 大概率能减少一些打包体积大小
- 将会减少一些'概念上'的知识点,入门更快
举个例子:
- 使用旧的转译支持
import React from 'react';
function App() {
return <h1>Hello World</h1>;
}
2
3
4
转译后:
import React from 'react';
function App() {
return React.createElement('h1', null, 'Hello world');
}
2
3
4
- 使用新的转译支持
function App() {
return <h1>Hello World</h1>;
}
2
3
转译后:
// Inserted by a compiler (don't import it yourself!)
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Hello world' });
}
2
3
4
5
# React 17.0.1
The React 17 release is unusual because it doesn’t add any new developer-facing features. Instead, this release is primarily focused on making it easier to upgrade React itself. In particular, React 17 is a “stepping stone” release that makes it safer to embed a tree managed by one version of React inside a tree managed by a different version of React. It also makes it easier to embed React into apps built with other technologies.
# ToDo
- React Profiler[12]
- Fiber Arth: https://www.jianshu.com/p/863e3c9da4c4