Change My World by Program

0%

React 开发手册

基本概念

  • React是一个用于构建用户界面的Javascript库,是DOM的一个抽象层
  • React主要用于构建UI

状态管理

State

Props

Effect Hook

  • 副作用:数据获取、设置订阅、手动更改DOM
  • 可以把useEffect Hook看作componentDidMount、componentDidUpdate、componentWillUnmount这三个函数的组合
  • 默认useEffect在第一次渲染之后和每次更新之后都会执行
  • 在函数组件中执行副作用操作,可以直接使用state,不用编写class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, { useState, useEffect } from 'react';

function Example() {
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>
);
}

路由

  • 主要使用的是React Router,包括react-routerreact-router-domreact-router-native
1
2
3
4
5
6
7
8
9
10
11
12
13
import { 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路径

组件

组件属性

  • 动态添加元素样式style

    1
    <div style={{display: this.state.show ? "block" : "none"}}>动态样式</div>
  • 动态添加类class

    1
    <div className={this.state.show ? "show-class" : "hide-class"}>动态类</div>

常用组件

Prompt

  • 在路由即将切换前弹出确认框(离开前确认)
  • 需要注意的是,如果用户点击了取消,那么会组织路由的切换,但是用户的点击事件如果有监听,依然会触发点击事件的
  • 最好在message里面判断是否需要展示Prompt,不要在when里面,因为在when里面,每次重新render都会执行,但是message里面只是在用户真的打算跳转的时候才会执行
1
2
3
<Prompt 
when={true}
message={(params) => params.pathname == '/当前路径' ? true : "确认离开" } /> // 当返回文字的时候会弹出确认,而返回true的时候则不会弹出

JSX语法

基本语法

1
2
<div tabIndex="0">ttt</div>	// 可以使用双引号来直接指定属性值
<img src={user.avatarUrl}</img> // 也可以使用大括号来指定变量属性值

代码片段fragments

  • 为一个组件添加元素,并且不会在DOM中增加额外的节点,常规的做法是在外层包一个div,这样就会多一层DOM元素,为了减少元素数量,可以这样做
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);

// 简写语法<>
return (
<>
<td>mycontent</td>
<td>mycontent</td>
</>
)

条件渲染

如果是在JSX外部的js部分代码,那么直接使用js自己的if或者其他条件判断即可完成。在JSX内部的话一般则是使用逻辑与&&或者三目运算符完成。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
render() {
return (
<div>
{this.state.posts.length > 0 &&
<p>There is some posts</p>
}
<p>
{length > 0 && '还可以这样直接写文字'}
</p>
</div>
<div>
<p>There is {this.state.posts !== undefined ? this.state.posts.length : 0} posts.</p>
</div>
)
}

必备三方组件

React-Redux

  • 从后端的角度看,就是一个维护全局变量的东西

  • Action定义了要发生什么,并且携带着数据,reducer用来定义发生该事情后需要做什么,selector可以理解是从state获取数据的API。

  • Redux可以通过connect方法,将storedispatch方法保存到组件的props

  • stateprops的对应通常需要使用mapStateToProps这个函数进行定义。它默认会订阅Store,每当state更新的时候,就会自动执行,重新计算UI组件的参数

  • 下面的方法在跟组件外面包了一层Provider,这样所有的子组件默认都能拿到store

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import rerducer from './reducers'
import App from './App'

const store = createStore(
reducer
);

ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
  • 可以用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
    16
    import {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
    3
    function* onGetSuccess(state) {
    const res = yield fetch("https://example.com").then(response => response.json());
    }

Redux-actions

redux-actionsreact-sage配合使用可以简化大量的重复代码。在之前我们要创建一个action需要线这样子定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 在使用redux-actions之前,需要这样创建一个action并使用
export const saveResult = (resultList) => {
console.log(resultList);
return {
type: "SAVE_RESULT",
resultList
}
};

// 现在只需要这样定义多个动作
export const actionCreators = createActions({
["SAVE_RESULT"]: resultList=>resultList,
});

Styled-Components

  • 目的是将React组件包装成Styled组件
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Card } from 'antd';

const StyledComponent = styled(Card)` // 可以接收一个React-Componetn例如Card,也可以接收一个tagName例如div
div {
color: red;
}
.abc {
color: blue;
width: ${props => props.width} // 变量传递
}
`
// 在render里面就可以直接使用该组件了
<StyledComponent width={"12px"}></StyledComponent>

SWR

  • state-while-revalidate的缩写,是HTTP RFC 5861中描述的一种Cache-Control扩展
  • ReactHook组件,用于缓存从远端获取的数据
  • 常用语多次请求相同URL获取数据,或者更新数据后,先返回缓存的响应,与此同时去后台发送新的请求,以提高响应速度,减少等待时间
  • 请求的结果根据标识key放在redux里缓存,取数据的时候直接从redux里面取
  • SWR并不是一个请求库,只是一种管理数据的方式
1
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
import useSWR from 'swr';

// 请求参数
// key: 表示请求的标识,可以是任意字符串,但是我们一般会设置为请求的url,这样方便识别。如果key传入null就代表不请求数据,什么都不做
// fetcher: 返回请求数据的异步方法,一般会是一个axios对象
// options: 其它配置项
// 返回值
// data: 相应数据
// error: 错误
// isValidating: 是否正在请求或重新验证数据
// mutate(data?, shouldRevalidate): 用于直接修改缓存数据
const { data, error, isValidating, mutate } = useSWR(key, fetcher, options);


const fetcher = Axios.create({
baseURL: 'https://haofly.net/api/',
responseType: "json",
});

const { data: user } = useSWR('/user', fetcher); // SWR会将key作为参数传递给fetcher
const { data: users, mutate } = useSWR(['/users', userIds], fetcher); // 可以传递多个参数,swr会将多个参数组合为一个key

import useSWR, { mutate } from 'swr';
mutate('/users'); // 手动再次获取数据,会让所有拥有相同key的swr主动去获取一次数据,并更新缓存

mutate(); // 或者直接用useSWR返回的mutate,可以省略key

mutate('/users', {...users, new: true}); // 触发更新操作,但是在更新操作完成前先直接用第二个参数来代替缓存,相当于先直接修改缓存
mutate({...users, new: true}); // 使用返回的mutate,省略key

const { newDate } = await axios.patch('/users'); // 更新users操作,直接返回新的数据
mutate(updated, false); // 如果更新操作直接返回更新后的资源,那么mutate可以直接使用它,然而最后一个参数设置为false,这样就可以不用去请求获取新的资源了,表示无需重新验证资源

事件

支持事件列表

1
2
3
// 焦点事件
onBlur
onFocus

TroubleShooting

  • React 表达式必须有一个父元素: 常出现在JSX的渲染的内嵌语句中返回了错误格式的结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    render() {
    return (
    {
    [ // 需要返回的应该是数组而不是混搭
    <p>abc</p>,
    <p>def</p>,
    ]
    })
    }
扩展阅读
坚持原创技术分享,谢谢支持

欢迎关注我的其它发布渠道