React学习笔记

React 的学习笔记

记录一些学习 React 路上的问题或者技巧。

React 解析

setState

setState 是一个异步函数,可以传入两个参数,第一个是改变的值,第二个是修改的回调函数。setState 内部有机制,可以将几个 setState 合并执行,提升效率,因此直接如下写法:

1
this.setState({ name: "name" });

然后立即取得 name 的值,很可能并无变化。使用传入状态计算函数就可以让 setState 立即执行,如下:

1
2
3
this.setState(function(prevState, props) {
return { showForm: !prevState.showForm };
});

redux -> reducer 为什么不能在 reducer 里面直接修改 state

在 redux-devtools 中,我们可以查看到 redux 下所有通过 reducer 更新 state 的记录,每一个记录都对应着内存中某一个具体的 state,让用户可以追溯到每一次历史操作产生与执行时,当时的具体状态,这也是使用 redux 管理状态的重要优势之一.
若不创建副本,redux 的所有操作都将指向内存中的同一个 state,我们将无从获取每一次操作前后,state 的具体状态与改变,若没有副本,redux-devtools 列表里所有的 state 都将被最后一次操作的结果所取代.我们将无法追溯 state 变更的历史记录.创建副本也是为了保证向下传入的 this.props 与 nextProps 能得到正确的值,以便我们能够利用前后 props 的改变情况以决定如何 render 组件

还有 reducer 必须是纯函数,好像不能使用 Math、Date 之类的函数。

纯函数:给定固定的输入就一定有固定的输出,且无副作用。

关于 Redux 框架,Reducer 中 state 处理方式的探讨

为什么要使用 actionTypes

首先是大家都这么写,这是一个默认的规范。其次最主要是使用这种方式可以有错误提示,而直接使用字符串无错误提示。

redux-thunk 的使用

yarn add redux-thunk
import {createStore,applyMiddleware,compose} from 'redux'
import thunk from 'redux-thunk'

1
2
3
4
5
6
7
8
9
10
const composeEnhancers =
typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
})
: compose;

const enhancer = composeEnhancers(applyMiddleware(thunk));

const store = createStore(reducer, enhancer);

如何使用:返回一个函数 外部调用此 action 的时候会执行这个 action

1
2
3
4
5
6
7
8
9
import axios from "axios";
export const getTodoList = () => {
return dispatch => {
axios.get("/name.json").then(({ data } = res) => {
const action = initAction();
dispatch(action);
});
};
};

React Hooks

自定义 React Hooks

自定义一个 hook 挺简单的,其实就是一个函数

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";

const useMousePositions = () => {
const [positions, setPositions] = useState({ x: 0, y: 0 });
useEffect(() => {
const updateMouse = event => {
setPositions({
x: event.clientX,
y: event.clientY
});
};
document.addEventListener("mousemove", updateMouse);
return () => {
document.removeEventListener("mousemove", updateMouse);
};
});
return positions;
};

export default useMousePositions;

使用的时候

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
33
import React, { useEffect, useState } from "react";
import useMousePositions from "../hooks/useMousePositions";

const MouseTracker = () => {
const [positions, setPositions] = useState({ x: 0, y: 0 });
const mousePositions = useMousePositions();
useEffect(() => {
const updateMouse = event => {
console.log("inner");

setPositions({
x: event.clientX,
y: event.clientY
});
};

console.log("add listener");
document.addEventListener("click", updateMouse);

return () => {
console.log("move listener");
document.removeEventListener("click", updateMouse);
};
});
return (
<p>
X:{mousePositions.x}
x:{positions.x} y:{positions.y}
</p>
);
};

export default MouseTracker;

定义一个 hook 并传入 props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React from "react";
import { Input } from "antd";

const { Search } = Input;

interface FileSearchProps {
title: string;
}

const FileSearch: React.FC<FileSearchProps> = ({ title }) => {
return (
<div>
<Search placeholder="搜索" loading={false} enterButton />
</div>
);
};

export default FileSearch;

 常用技巧

react 使用 lib-flexible 和 postcss-px2rem 开心的适配

参考文章

react 基础环境搭建

React + TS + Less

  1. yarn eject

  2. yarn add less less-loader --dev

  3. config -> webpack.config.js 里面添加

    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
    const lessRegex = /\.(less)$/;
    const lessModuleRegex = /\.module\.(less)$/;

    {
    test: lessRegex,
    exclude: lessModuleRegex,
    use: getStyleLoaders({
    importLoaders: 2,
    sourceMap: isEnvProduction && shouldUseSourceMap,
    },
    'less-loader'
    ),
    // Don't consider CSS imports dead code even if the
    // containing package claims to have no side effects.
    // Remove this when webpack adds a warning or an error for this.
    // See https://github.com/webpack/webpack/issues/6571
    sideEffects: true,
    },
    // Adds support for CSS Modules, but using less
    // using the extension .module.scss or .module.less
    {
    test: lessModuleRegex,
    use: getStyleLoaders({
    importLoaders: 2,
    sourceMap: isEnvProduction && shouldUseSourceMap,
    modules: true,
    camelCase: true,
    getLocalIdent: getCSSModuleLocalIdent,
    },
    'less-loader'
    ),
    },

    以上两段代码比照着 sass 在配置里面添加

  4. 如果是 ts 项目 还需要在 src 根目录下面加一个 typings 文件夹 然后添加对应的定义问题 例如: styles.d.ts

    1
    2
    declare module "*.css";
    declare module "*.less";
  5. camelCase: true,这个属性 用来处理导出的*.module.less 横杠写法为驼峰式写法。是css-loader里面的属性配置,其余属性看文档。

常见问题

用 TS 写 React

  1. TS 写 React 要按照约定在 extends React.Component 的时候接收两个泛型,第一个是 Prop 泛型,第二个是 State 泛型,这样在接下来的代码中才可以使用定义的数据。

    1
    2
    3
    4
    interface HeaderState {
    searchBoxActivated: boolean;
    }
    class Header extends Component<any, HeaderState> {}
  2. TS 项目中遇到引入样式或者 Module 报错的情况,在根目录下创建对应的*.d.ts 文件定义模块。style.d.ts declare module "*.module.styl"; module.d.ts declare module "react-transition-group";

  3. 以下代码报错

    1
    2
    const composeEnhancers =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

    解决方式如下:

    1
    2
    const composeEnhancers =
    (<any>window).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

第三方库

  1. classnames 可以合并class

第三方库问题

  1. 若要同时使用 CSS-Module 和react-transition-group库,定义的 className 会不生效。
    解决办法:对应的 jsx 文件同时引入style.stylstyle.module.styl文件,并且修改的样式要写在 styl 文件而不要写在 module.styl 文件,不然插件不会生效。
Author: XavierShi
Link: https://blog.xaviershi.com/2019/05/04/React学习笔记/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.