本文目录:
- 1.React入口
- 2.JSX语法
- 3.组件
- 4.正确使用setState
- 5.生命周期
- 6.组件复合
- 7.redux
- 8.react-redux
- 9.react-router
- 10.PureComponent
- 11.认识Hook
- 12.自定义Hook与Hook使用规则
- 13.Hook API之useMemo与useCallback
参考网址:https://zh-hans.reactjs.org/docs/getting-started.html
1.React入口
起步
- 创建项目: npx create-react-app my-app
- 打开项目: cd my-app
- 启动项目: npm start
- 暴露配置项: npm run eject
注意:暴露配置项操作是不可逆的
cra文件结构
├── README.md 文档
├── public 静态资源
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src 源码
├── App.css
├── App.js 根组件
├── App.test.js
├── index.css 全局样式
├── index.js 入口文文件
├── logo.svg
└── serviceWorker.js pwa支持
├── package.json npm 依赖
入口文件定义,webpack.config.js
entry: [
// WebpackDevServer客户端,它实现开发时热更新功能
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
// 应用程序入口:src/index
paths.appIndexJs,
].filter(Boolean),
webpack.config.js 是webpack配置⽂文件,开头的常量声明可以看出cra能够支持ts、sass及css模块化。
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
React和ReactDom
删除src下面所有代码,新建index.js。
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(Hello React
, document.querySelector('#root'));
React负责逻辑控制,数据 -> VDOM
ReactDom渲染实际DOM,VDOM -> DOM
React使用JSX来描述UI
babel-loader把JSX 编译成相应的 JS 对象,React.createElement再把这个JS对象构造成React需要的虚拟dom。
2.JSX语法
JSX是一种JavaScript的语法扩展,其格式⽐比较像模版语言,但事实上完全是在JavaScript内部实现的。
JSX可以很好地描述UI,能够有效提高开发效率,
2.1.基本使用
表达式{}的使用,index.js
const name = "react study";
const jsx = hello, {name};
2.2.函数
函数也是合法表达式,index.js
const obj = {
fistName: "Harry",
lastName: "Potter"
};
function formatName(name) {
return name.fistName + " " + name.lastName;
}
const jsx = {formatName(user)};
2.3.对象
jsx是js对象,也是合法表达式,index.js
const greet = good;
const jsx = {greet};
2.4.条件语句
条件语句可以基于上⾯结论实现,index.js
const show = true;//false;
const greet = good;
const jsx = (
{/* 条件语句 */}
{show ? greet : "登录"}
{show && greet}
);
2.5.数组
数组会被作为一组子元素对待,数组中存放一组jsx可用于显示列表数据
const a = [0, 1, 2];
const jsx = (
{/* 数组 */}
{/* diff时候,⾸首先⽐比较type,然后是key,所以同级同类型元素,key值必须得 唯一 */}
{a.map(item => (
- {item}
))}
);
2.6.属性的使用
import logo from "./logo.svg";
const jsx = (
{/* 属性:静态值用双引号,动态值用花括号;class、for等要特殊处理。 */}
);
2.7.模块化
css模块化,创建index.module.css,index.js
import style from "./index.module.css";
或者npm install sass -D
import style from "./index.module.scss";
更多css modules规则参考
http://www.ruanyifeng.com/blog/2016/06/css_modules.html
3.组件
组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
组件有两种形式:class组件和function组件。
3.1.class组件
class组件通常拥有状态和生命周期,继承于Component,实现render方法。用class组件创建⼀个Clock
import React, {
Component
} from "react";
export default class ClassComponent extends React.Component {
constructor(props) {
super(props);
// 使用state属性维护状态,在构造函数中初始化状态
this.state = {
date: new Date()
};
}
componentDidMount() {
// 组件挂载之后启动定时器每秒更新状态
this.timerID = setInterval(() => {
// 使用setState⽅方法更新状态
this.setState({
date: new Date()
});
}, 1000);
}
componentWillUnmount() {
// 组件卸载前停止定时器
clearInterval(this.timerID);
}
componentDidUpdate() {
console.log("componentDidUpdate");
}
render() {
return {
this.state.date.toLocaleTimeString()
} < /div>;
}
}
3.2.function组件
函数组件通常无状态,仅关注内容展示,返回渲染结果即可
从React16.8开始引入了hooks,函数组件也能够拥有状态。
用function组件创建一个Clock:
import React, { useState, useEffect } from "react";
export function FunctionComponent(props) {
const [date, setDate] = useState(new Date());
useEffect(() => {//副作用
const timer = setInterval(() => {
setDate(new Date());
}, 1000);
return () => clearInterval(timer);//组件卸载的时候执行
}, []);
return (
FunctionComponent
{date.toLocaleTimeString()}
);
}
提示: 如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做componentDidMount , componentDidUpdate 和componentWillUnmount 这三个函数的组合。
4.正确使用setState
setState(partialState, callback)
- partialState : object|function
用于产生与当前state合并的子集。
- callback : function
state更新之后被调用。
4.1.关于 setState() 你应该了了解三件事:
不要直接修改 State
例如,此代码不会重新渲染组件:
// 错误示范
this.state.comment = 'Hello';
⽽是应该使用 setState() :
// 正确使用
this.setState({comment: 'Hello'});
4.2.State 的更新可能是异步的
出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。
观察以下例例子中log的值和button显示的counter。
import React, {
Component
} from "react";
export default class SetStatePage extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
};
}
changeValue = v => {
this.setState({
counter: this.state.counter + v
});
console.log("counter", this.state.counter);
};
setCounter = () => {
this.changeValue(1);
//this.changeValue(2);
//console.log("counter", this.state.counter);
};
render() {
const {
counter
} = this.state;
return (
< div >
SetStatePage < /h3>
);
}
}
如果要获取到最新状态值有以下⽅式:
1.在回调中获取状态值
changeValue = v => {
this.setState({
counter: this.state.counter + v
},
() => {
console.log("counter", this.state.counter);
}
);
};
- 使用定时器:
setTimeout(() => {
this.setCounter();
}, 0);
- 原生事件中修改状态
componentDidMount(){
document.body.addEventListener('click', this.changeValue, false)
}
总结: setState只有在合成事件和生命周期函数中是异步的,在原生事件和setTimeout中都是同步的,这里的异步其实是批量更新。
4.3.State 的更新会被合并
changeValue = v => {
this.setState({
counter: this.state.counter + v
});
};
setCounter = () => {
this.changeValue(1);
this.changeValue(2);
};
此时的 this.changeValue(1); 不会生效
如果想要链式更新state:
changeValue = v => {
this.setState(state => ({
counter: state.counter + v
}));
};
setCounter = () => {
this.changeValue(1);
this.changeValue(2);
};
5.生命周期
参考文档:https://zh-hans.reactjs.org/docs/react-component.html#constructor
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
- constructor()
- static getDerivedStateFromProps()
- render()
- componentDidMount()
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapsBeforeUpdate()
- componentDidUpdate()
以下的三个生命周期函数将被废弃,用getDerivedStateFromProps代替,目前使用的话加上UNSAFE_:
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
引入两个新的生命周期函数
- static getDerivedStateFromProps
- getSnapshotBeforeUpdate
getDerivedStateFromProps
static getDerivedStateFromProps(props, state)
getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
请注意,不管原因是什么,都会在每次渲染前触发此方法。这与UNSAFE_componentWillReceiveProps 形成对比,后者仅在父组件重新渲染时触发,而不是在内部调用 setState 时。
getSnapshotBeforeUpdate
getSnapshotBeforeUpdate(prevProps, prevState)
在render之后,在componentDidUpdate之前。
getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate(prevProps, prevState, snapshot) 。
6.组件复合
复合组件给与你⾜够的敏捷去定义⾃定义组件的外观和行为,这种⽅式更明确和安全。如果组件间有公⽤的非UI逻辑,将它们抽取为JS模块导入使用而不是继承它。
不具名
新建一个layout.js文件
import React, {
Component
} from "react";
import TopBar from "../components/TopBar";
import BottomBar from "../components/BottomBar";
export default class Layout extends Component {
componentDidMount() {
const {
title = "商城"
} = this.props;
document.title = title;
}
render() {
const {
children,
showTopBar,
showBottomBar
} = this.props;
console.log("children", children);
return (
< div >
{showTopBar && }
{children}
{showBottomBar && }