React 常用插件
ESLint 配置
React 项目 ESLint
json
{
"env": { "browser": true, "node": true, "es6": true },
"parser": "babel-eslint",
"extends": ["react-app", "eslint:recommended", "plugin:react/recommended"],
"rules": {
"comma-dangle": [2, "never"],
"no-console": 0,
"react/jsx-key": 2,
"react/jsx-no-duplicate-props": 2,
"react/no-array-index-key": 2,
"react/no-direct-mutation-state": 2,
"react/self-closing-comp": 2
}
}{
"env": { "browser": true, "node": true, "es6": true },
"parser": "babel-eslint",
"extends": ["react-app", "eslint:recommended", "plugin:react/recommended"],
"rules": {
"comma-dangle": [2, "never"],
"no-console": 0,
"react/jsx-key": 2,
"react/jsx-no-duplicate-props": 2,
"react/no-array-index-key": 2,
"react/no-direct-mutation-state": 2,
"react/self-closing-comp": 2
}
}Parsing error: Unexpected token =
使用 babel-eslint 解析:
js
module.exports = {
parser: 'babel-eslint'
}module.exports = {
parser: 'babel-eslint'
}styled-components
通过组件形式写样式:
tsx
import styled from 'styled-components'
const Button = styled.button`
display: inline-block;
background: ${props => props.primary ? 'palevioletred' : 'white'};
color: ${props => props.primary ? 'white' : 'palevioletred'};
&:focus { background: #000; }
`import styled from 'styled-components'
const Button = styled.button`
display: inline-block;
background: ${props => props.primary ? 'palevioletred' : 'white'};
color: ${props => props.primary ? 'white' : 'palevioletred'};
&:focus { background: #000; }
`主题
tsx
import { ThemeProvider } from 'styled-components'
<ThemeProvider theme={{ main: 'mediumseagreen' }}>
<Button>Themed</Button>
</ThemeProvider>
// 函数组件获取主题
import { useTheme } from 'styled-components'
const theme = useTheme()import { ThemeProvider } from 'styled-components'
<ThemeProvider theme={{ main: 'mediumseagreen' }}>
<Button>Themed</Button>
</ThemeProvider>
// 函数组件获取主题
import { useTheme } from 'styled-components'
const theme = useTheme()Emotion(CSS-in-JS)
bash
yarn add @emotion/coreyarn add @emotion/corejsx
/** @jsx jsx */
import { css, jsx } from '@emotion/core'
<div css={css`
padding: 32px;
color: black;
&:hover { color: white; }
`} />/** @jsx jsx */
import { css, jsx } from '@emotion/core'
<div css={css`
padding: 32px;
color: black;
&:hover { color: white; }
`} />Mock 数据
mockjs
bash
npm i mockjsnpm i mockjsjs
import Mock from 'mockjs'
Mock.mock('/list', 'get', {
'object|2-4': { 110000: '北京市', 120000: '天津市' }
})import Mock from 'mockjs'
Mock.mock('/list', 'get', {
'object|2-4': { 110000: '北京市', 120000: '天津市' }
})msw + faker.js
bash
npx msw init public/
npm i @mswjs/data @faker-js/fakernpx msw init public/
npm i @mswjs/data @faker-js/fakerjs
import { setupWorker } from 'msw'
import { rest } from 'msw'
export const worker = setupWorker(
rest.get('/login', (req, res, ctx) => {
return res(ctx.json({ username: 'wing' }))
})
)
// 入口文件
if (process.env. NODE_ENV === 'development') {
const { worker } = require('./mocks/browser')
worker.start()
}import { setupWorker } from 'msw'
import { rest } from 'msw'
export const worker = setupWorker(
rest.get('/login', (req, res, ctx) => {
return res(ctx.json({ username: 'wing' }))
})
)
// 入口文件
if (process.env. NODE_ENV === 'development') {
const { worker } = require('./mocks/browser')
worker.start()
}动画
bash
npm i react-transition-groupnpm i react-transition-groupjsx
import { CSSTransition, TransitionGroup } from 'react-transition-group'
<CSSTransition in={flag} timeout={2000} classNames="fade" unmountOnExit>
<div>动画内容</div>
</CSSTransition>import { CSSTransition, TransitionGroup } from 'react-transition-group'
<CSSTransition in={flag} timeout={2000} classNames="fade" unmountOnExit>
<div>动画内容</div>
</CSSTransition>react-query(数据请求)
tsx
import { useQuery, useMutation, useQueryClient } from 'react-query'
// GET 请求
const { data, isLoading, refetch } = useQuery(
['todos', page],
() => fetch('/api/todos').then(res => res.json()),
{ enabled: true, retry: 3 }
)
// 其他请求
const mutation = useMutation(postTodo, {
onSuccess: () => queryClient.invalidateQueries('todos')
})
mutation.mutate({ title: '1234' })import { useQuery, useMutation, useQueryClient } from 'react-query'
// GET 请求
const { data, isLoading, refetch } = useQuery(
['todos', page],
() => fetch('/api/todos').then(res => res.json()),
{ enabled: true, retry: 3 }
)
// 其他请求
const mutation = useMutation(postTodo, {
onSuccess: () => queryClient.invalidateQueries('todos')
})
mutation.mutate({ title: '1234' })Echarts
echarts-for-react— React 中简单使用 Echartsecharts-wordcloud— 词云图
二维码
bash
npm install qrcode.reactnpm install qrcode.reactjsx
import { QRCodeSVG } from 'qrcode.react'
<QRCodeSVG value="https://reactjs.org/" />import { QRCodeSVG } from 'qrcode.react'
<QRCodeSVG value="https://reactjs.org/" />其他插件
| 插件 | 说明 |
|---|---|
react-lazyload | 图片/组件懒加载 |
react-scroll | 滚动控制 |
react-router-cache-route | 路由缓存 |
classnames | 动态 class |
react-fontawesome | SVG 图标库 |
react-cropper | 图片裁剪 |
react-split-pane | 面板拖拽 |
memoize-one | 记忆函数(相同参数返回上次结果) |
mitt | eventBus 事件监听 |
formik + yup | 表单校验 |
@ebay/nice-modal-react | Modal 管理 |
Mobx
安装
bash
npm i mobx mobx-reactnpm i mobx mobx-reactStore
js
import { observable, action, configure, runInAction } from 'mobx'
configure({ enforceActions: 'always' })
class Store {
@observable number = 0
@action add = () => { this.number++ }
@action load = async () => {
const data = await getData()
runInAction(() => { this.name = data.name })
}
}
export default new Store()import { observable, action, configure, runInAction } from 'mobx'
configure({ enforceActions: 'always' })
class Store {
@observable number = 0
@action add = () => { this.number++ }
@action load = async () => {
const data = await getData()
runInAction(() => { this.name = data.name })
}
}
export default new Store()组件中使用
jsx
import { observer, inject } from 'mobx-react'
@inject('store')
@observer
class Child extends Component {
render() {
return <div>{this.props.store.number}</div>
}
}import { observer, inject } from 'mobx-react'
@inject('store')
@observer
class Child extends Component {
render() {
return <div>{this.props.store.number}</div>
}
}支持装饰器
js
// .babelrc
{
"plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }]]
}// .babelrc
{
"plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }]]
}Redux-Saga
jsx
import { takeEvery, put } from 'redux-saga/effects'
function* mySagas() {
yield takeEvery('GET_LIST', getList)
}
function* getList() {
const res = yield new Promise(resolve => {
setTimeout(() => resolve(['111', '222']), 2000)
})
yield put({ type: 'CHANGE_LIST', payload: res })
}import { takeEvery, put } from 'redux-saga/effects'
function* mySagas() {
yield takeEvery('GET_LIST', getList)
}
function* getList() {
const res = yield new Promise(resolve => {
setTimeout(() => resolve(['111', '222']), 2000)
})
yield put({ type: 'CHANGE_LIST', payload: res })
}
Cao Note