基本概念
React
是一个用于构建用户界面的Javascript库,是DOM
的一个抽象层React
主要用于构建UI
状态管理
State
Props
props.children,类似于
vue
种的槽,只要组件内部有元素,那么它就是props.children,内部可以直接拿来用,例如:1
2
3
4
5
6
7
8
9
10
11<MyComponent>
<div>test</div>
</MyComponent>
function MyComponent(props) {
return (
<div>
{props.children}
</div>
)
}
Effect Hook
副作用:数据获取、设置订阅、手动更改DOM
可以把
useEffect Hook
看作componentDidMount、componentDidUpdate、componentWillUnmount
这三个函数的组合,组件渲染完成后执行某些操作官方建议一个组件中不同的功能最好分开写
useEffect
默认
useEffect
每次重新渲染都会执行一次,可以传入第二个参数来控制渲染的次数:- 不传第二个参数,每次render都会执行
- 传入空数组,只会执行一次
- 传入一个值,当那个值改变的时候就执行
- 传入多个值,当其中某个值改变的时候就执行
1
useEffect(() => {}, [props.user]) // 这样当props.user改变的时候能够重新执行一次函数
副作用清除机制,在useEffect中返回一个函数,能够有效防止内存溢出等异常:
1
2
3
4
5
6useEffect(() => {
Client.subscribe(args, callBackFunc()); // 如果我们需要在渲染完成后进行订阅
return function cleanup () { // 如果需要在组件卸载的时候退出订阅就能这样做
Client.unsubscribe();
}
})在函数组件中执行副作用操作,可以直接使用
state
,不用编写class
1 | import React, { useState, useEffect } from 'react'; |
Ref
1 | const ref = useRef(null) |
Context
- 和
Redux
类似,也有一个Provider
在最外面 - Context更适合存储全局的一些属性,例如用户选择的语言、地区偏好、UI主题等
- 还是觉得
Redux
方便好理解一点,而且功能强大一点,Redux
才是做了一个完整的状态管理功能,context
主要就是存储一个全局的数据,当然使用了context数据的的UI组件在context数据变化时也会刷新,是整个UI树的更新,用户体验和性能都会有问题 - 使用Context不用从最父级的组件一层一层往下传递了
1 | const WebContext: Context<boolean> = createContext<boolean>(true); |
路由
主要使用的是
React Router
,包括react-router
,react-router-dom
,react-router-native
1
2
3
4
5
6
7
8
9
10
11
12
13import { Switch, Route } from 'react-router-dom'
const Main = () => (
<main>
<Switch>
<Route path='/roster' component={Home}/>
<Route path='/schedule' component={Post}/>
<Route path='/about', render={() => <About something={this.something}>}/> <!--通过这种方式绑定数据或者方法给子路由-->
</Switch>
</main>
)
this.props.location.pathname; // 获取当前的url路径需要注意的是,如果是
Link
链接的路由和当前路由是一样的,那么页面不会发生跳转,什么都不会做,这时候如果是弹出菜单,弹出菜单也不会自动关闭,所以这种情况可以单独处理一下,用a
标签代替一下,然后使用window.location.href来进行跳转吧,例如:1
2
3
4
5
6function jumpToMenu(url, e) {
e.preventDefault()
window.location.href = url
}
<a href={url} onClick={(e) => jumpToMenu(url, e)}>{text}</a> // 在onClick里面传递参数
组件
组件定义
函数组件
- 函数组件中能使用
useState
1 | // typescript中需要这样定义,FC是TypeScript使用的一个范性,意思是FunctionComponent |
组件属性
动态添加元素样式
style
1
<div style={{display: this.state.show ? "block" : "none"}}>动态样式</div>
动态添加类/条件渲染类
class
1
2<div className={this.state.show ? "show-class" : "hide-class"}>动态类</div>
<div className={style.style1 + ' ' + style.style2}>多个类</div>
常用组件
Prompt
- 在路由即将切换前弹出确认框(离开前确认)
- 需要注意的是,如果用户点击了取消,那么会组织路由的切换,但是用户的点击事件如果有监听,依然会触发点击事件的
- 最好在
message
里面判断是否需要展示Prompt
,不要在when
里面,因为在when
里面,每次重新render
都会执行,但是message
里面只是在用户真的打算跳转的时候才会执行
1 | <Prompt |
父子组件资源共享
父组件传递props给子组件,可以直接在父组件修改,会更新给子组件的
父组件将方法作为props传递给子组件
1 | // 父组件 |
JSX语法
基本语法
1 | <div tabIndex="0">ttt</div> // 可以使用双引号来直接指定属性值 |
代码片段fragments
- 为一个组件添加元素,并且不会在DOM中增加额外的节点,常规的做法是在外层包一个
div
,这样就会多一层DOM元素,为了减少元素数量,可以这样做
1 | return ( |
条件渲染
- 如果是在
JSX
外部的js
部分代码,那么直接使用js
自己的if
或者其他条件判断即可完成。在JSX
内部的话一般则是使用逻辑与&&
或者三目运算符完成。例如 - 如果条件渲染的结果全是字符串0,那么应该是条件没有转换为布尔值,可以使用双感叹号来转换,例如!!this.posts
1 | render() { |
map循环
1 | <Nav> |
必备三方组件
prop-types
- 类似于typescript,能够有效限制与定义组件中的prop参数类型
- 不过建议在pages页面(非component)中禁用eslint的
react/prop-types: 'off'
功能,因为页面的props可能太多,也和component中的有重复
1 | import PropTypes from 'prop-types'; |
React-Redux
- 从后端的角度看,就是一个维护全局变量的东西(也有同一个页面不同组件使用相同的东西,比如当前用户的用户名用户头像啥的)
Action
定义了要发生什么,并且携带着数据(可以在action
里面调用API,将结果进行dispatch
,类似于vuex中的action
将结果进行mutation
),reducer
用来定义发生该事情后需要做什么(类似于vuex中的mutation),selector
可以理解是从state
获取数据的API。Redux
可以通过connect
方法,将store
的dispatch
方法保存到组件的props
中state
与props
的对应通常需要使用mapStateToProps
这个函数进行定义。它默认会订阅Store
,每当state
更新的时候,就会自动执行,重新计算UI组件的参数- 下面的方法在根组件外面包了一层
Provider
,这样所有的子组件默认都能拿到store
了 - 注意如果使用useselect等在第一次没有值,页面第二次渲染才有值,可能是因为没有将这些状态持久化造成的
1 | import { Provider } from 'react-redux' |
- 可以用
connect
将组件与store
进行连接,它可以接收四个参数:mapStateToProps(state, ownProps) : stateProps
,第一个参数就是Redus的store
,这个方法的返回值就会作为组件的props
Redux-Saga
从后端的角度看,就是在启动应用的时候,再启动一些单独的线程,这些线程可以异步去做些更改变量或者监听的事情,类似于钩子。
可以在这里对异步操作进行集中处理。
Effect
的几种方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import {take,call,put,select,fork,takeEvery,takeLatest} from 'redux-saga/effects'
// take: 用来监听action,返回监听到的action对象,例如如果有一个type=login的action,那么在执行dispatch(loginAction)之后,就可以这样获取对象
const action = yield take('login')
// call: 调用指定函数
yield call(myfunc, param1, param2);
// put: 触发dispatch,用于发送action,dispatch in saga
yield put({type: 'login'})
// select: 和getState类似,用于获取store中的state
const state = yield select()
// fork
// takeEvery/takeLatest: 用于监听简单的例子:
1
2
3
4
5
6
7
8
9
10
11// 在编写saga的文件里面一般这样写钩子函数
export function * mySaga(action) { // 这里的参数action,是包含了payload,state的对象
console.log("Hello Saga");
}
// 在main.js中,这样引入saga中间件
import createSagaMiddleware from 'redux-saga';
import {helloSaga} from './saga.js';
const sageMiddleware = createSagaMiddleware();
const store = createStore(rerducer, aplyMiddleware(sagaMiddleware));
sagaMiddleware.run(helloSaga);saga
中使用fetch
1
2
3function* onGetSuccess(state) {
const res = yield fetch("https://example.com").then(response => response.json());
}
Redux-actions
将redux-actions
和react-saga
配合使用可以简化大量的重复代码。在之前我们要创建一个action
需要线这样子定义:
1 | // 在使用redux-actions之前,需要这样创建一个action并使用 |
Styled-Components
- 目的是将
React
组件包装成Styled
组件
1 | import { Card } from 'antd'; |
SWR
state-while-revalidate
的缩写,是HTTP RFC 5861
中描述的一种Cache-Control
扩展React
的Hook
组件,用于缓存从远端获取的数据- 常用语多次请求相同URL获取数据,或者更新数据后,先返回缓存的响应,与此同时去后台发送新的请求,以提高响应速度,减少等待时间
- 请求的结果根据标识
key
放在redux
里缓存,取数据的时候直接从redux
里面取 SWR
并不是一个请求库,只是一种管理数据的方式
1 | import useSWR from 'swr'; |
事件
支持事件列表
1 | // 监听全局scroll事件 |
TroubleShooting
引入json格式的文件: 可以直接
import data from 'path/to/my.json'
React 表达式必须有一个父元素: 常出现在
JSX
的渲染的内嵌语句中返回了错误格式的结果:1
2
3
4
5
6
7
8
9render() {
return (
{
[ // 需要返回的应该是数组而不是混搭
<p>abc</p>,
<p>def</p>,
]
})
}