React 进阶
Junfeng Liu
2018-05-11
Return type of render() method
- null. Renders nothing.
-
Booleans. Renders nothing.
(To support return test && <Child /> pattern.) - String and numbers. These are rendered as text nodes in the DOM.
- Portals. Created with ReactDOM.createPortal.
- React element. Typically created via JSX.
- Array. Return multiple items, since React 16.0.
- Fragment. Since React 16.2.
Fragments
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
// Short syntax available in Babel v7.0.0-beta.31+
class Columns extends React.Component {
render() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
}
Portals
render() {
// React does *not* create a new div. It renders the children into `domNode`.
// `domNode` is any valid DOM node, regardless of its location in the DOM.
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
New context API in React 16.3
const ThemeContext = React.createContext('light');
class ThemeProvider extends React.Component {
state = {theme: 'light'};
render() {
return (
<ThemeContext.Provider value={this.state.theme}>
{this.props.children}
</ThemeContext.Provider>
);
}
}
class ThemedButton extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{theme => <Button theme={theme} />}
</ThemeContext.Consumer>
);
}
}
New React lifecycle methods
Higher Order Components
- Props Proxy
function ppHOC(WrappedComponent) {
return class PP extends React.Component {
render() {
const newProps = {
user: currentLoggedInUser
}
return <WrappedComponent {...this.props} {...newProps}/>
}
}
}
Higher Order Components
- Inheritance Inversion
function iiHOC(WrappedComponent) {
return class Enhancer extends WrappedComponent {
render() {
if (this.props.loggedIn) {
return super.render()
} else {
return null
}
}
}
}
Higher Order Components
- HOC and parameters
function HOCFactoryFactory(...params){
// do something with params
return function HOCFactory(WrappedComponent) {
return class HOC extends React.Component {
render() {
return <WrappedComponent {...this.props}/>
}
}
}
}
HOCFactoryFactory(params)(WrappedComponent)
Function as Child Component
const Foo = ({ children }) => {
return children('foo');
};
<Foo>
{(name) => <div>`hello from ${name}`</div>}
</Foo>
Function as Prop Component
const Foo = ({ hello }) => {
return hello('foo');
};
const hello = (name) => {
return <div>`hello from ${name}`</div>;
};
<Foo hello={hello} />
Component Injection
const Foo = ({ Hello }) => {
return <Hello name="foo" />;
};
const Hello = ({ name }) => {
return <div>`hello from ${name}`</div>;
};
<Foo Hello={Hello} />
Functional setState
setState(stateChange[, callback])
this.setState({quantity: 2});
performs a shallow merge of stateChange into the new state
setState(updater[, callback])
this.setState((prevState, props) => {
return {counter: prevState.counter + props.step};
});
updater is a function with the signature:
(prevState, props) => stateChange
Functional setState
function increase (state, props) {
return {value : state.value + props.step};
}
function decrease (state, props) {
return {value : state.value - props.step};
}
class Counter extends Component {
state = {value: 0};
handleIncrement = () => {
this.setState(increase);
}
handleDecrement = () => {
this.setState(decrease);
}
render() {
return (
<div>
<button onClick={this.handleIncrement}>+</button>
<h1>{this.state.value}</h1>
<button onClick={this.handleDecrement}>-</button>
</div>
);
}
}
Declare state changes separately from the component classes.
CSS in JS
写 CSS 代码时都有哪些痛点:
- 全局污染 – CSS 的选择器是全局生效的,所以在 class 名称比较简单时,容易引起全局选择器冲突,导致样式互相影响。
- 命名混乱 – 因为怕全局污染,所以日常起 class 名称时会尽量加长,这样不容易重复,但当项目由多人维护时,很容易导致命名风格不统一。
- 样式重用困难 – 有时虽然知道项目上已有一些相似的样式,但因为怕互相影响,不敢重用。
- 代码冗余 – 由于样式重用的困难性等问题,导致代码冗余。
CSS in JS
-
Radium: https://github.com/FormidableLabs/radium
-
Aphrodite: https://github.com/Khan/aphrodite
-
emotion: https://github.com/tkh44/emotion
- glamorous: https://github.com/paypal/glamorous
-
glamor: https://github.com/threepointone/glamor
-
styled-jsx: https://github.com/zeit/styled-jsx
-
jsxstyle: https://github.com/smyte/jsxstyle
-
TypeStyle: https://github.com/typestyle/typestyle
-
styletron: https://github.com/rtsao/styletron
-
styled-components: https://github.com/styled-components/styled-components
- ...
styled-components
import React from 'react';
import styled, { css } from 'styled-components';
// Create a <Title> react component
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
// Create a <Wrapper> react component that renders a <section> with
// some padding and a papayawhip background
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
// Use them like any other React component – except they're styled!
<Wrapper>
<Title>Hello World, this is my first styled component!</Title>
</Wrapper>
const Button = styled.button`
border-radius: 3px;
padding: 0.25em 1em;
margin: 0 1em;
background: transparent;
color: palevioletred;
border: 2px solid palevioletred;
${props => props.primary && css`
background: palevioletred;
color: white;
`}
`;
const Link = ({ className, children }) => (
<a className={className}>
{children}
</a>
)
const StyledLink = styled(Link)`
color: palevioletred;
font-weight: bold;
`;
render(
<div>
<Link>Unstyled, boring Link</Link>
<br />
<StyledLink>Styled, exciting Link</StyledLink>
</div>
);
https://www.styled-components.com/
styled-components
Lightweight React Alternatives
名称 | 大小 (gzip) | 特点 |
---|---|---|
React | 49KB | |
Preact | 3KB | 体积小,高性能,兼容 IE9+ |
Inferno | 8KB | 高性能,无状态组件支持生命期事件 |
react-lite | 13KB | 对 React 的兼容程度高,只支持 JSX |
Nerv | 9KB | 京东开发,高性能,兼容 IE8 |
从 React 切换到 Nerv
{
resolve: {
alias: {
'react': 'nervjs',
'react-dom': 'nervjs'
}
}
}
- Webpack
{
"plugins": [
["module-resolver", {
"root": ["."],
"alias": {
"react": "nervjs",
"react-dom": "nervjs"
}
}]
]
}
- Babel
Parcel bundler
- 零配置
- 编译速度更快
- 热更新
yarn global add parcel-bundler
parcel index.html
yarn add --dev parcel-bundler
// package.json
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
}
yarn start
<html>
<body>
<script src="./index.js"></script>
</body>
</html>
index.html
Storybook
- 开发过程中即时预览 React 组件
- 可绕过 UI 跳转逻辑,伪造输入参数
- 有很多的插件
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import Button from '../components/Button';
storiesOf('Button', module)
.add('with text', () => (
<Button onClick={action('clicked')}>Hello Button</Button>
))
.add('with some emoji', () => (
<Button onClick={action('clicked')}>
<span role="img" aria-label="so cool">😀 😎 👍 💯</span>
</Button>
));
前端工程
- 打包:速度快,支持热更新
- 配置文件:不同的配置环境导出不同的内容
- 前端路由
- REST API 调用
- GraphQL 调用
- 对话框:Alert, Toast, Modal
- 矢量图标: 插入 SVG,并可用 CSS 修改样式
- 移动端适配方案
参考资料:
React 进阶
By Liu Junfeng
React 进阶
- 603