文章目录
state
不能直接修改state,需借助setState()
setState(partialState, callback)
- partialState: object | Function
用于产生与当前state合并的子集。 - callback: Function
state更新之后被调用。
state 的更新可能是异步
- setState在
合成事件
和生命周期
中是异步的,这里说的异步是批量更新,达到优化性能的目的 - setState在setTimeout中是
同步
的
setData = () => {
// 同步方式
setTimeout(() => {
this.changeValue()
}, 0)
}
changeValue = () => {
this.setState({data: this.state.data + 1})
}
- setState在 原生事件 中是
同步
的
document.getElementById("test").addEventListener("click", this.setData)
state 的更新会被合并
setData = () => {
this.changeValue()
this.changeValue()
} // data 只会加1,不会加2
修改 changeValue 方法
changeValue = () => {
this.setState(state => { // 接收的Function类型
return {
data: state.data + 1
}
})
}
redux
检查点:
- createStore 创建store
- reducer 初始化、修改状态函数
- getState 获取状态值
- dispatch 提交更新
- subscribe 变更订阅
// pages/ReduxDemo.js
import * as React from 'react'
import store from '../store/index'
class ReduxDemo extends React.Component{
constructor(props) {
super(props);
this.state = {
counter: store.getState()
}
console.log(store.getState())
}
componentDidMount() {
// store 订阅,store的值发生变化时触发
store.subscribe(() => {
this.setState({counter: store.getState()})
})
}
render() {
return (
<div>
<p>{this.state.counter}</p>
<button onClick={() => {store.dispatch({type: 'ADD'})}}>ADD</button>
</div>
)
}
}
export default ReduxDemo
// store/index.js
import {createStore} from "redux";
// 定义state初始化和修改规则
function counterReducer(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + 1
default:
return state
}
}
const store = createStore(counterReducer)
export default store
react-redux
专为react设计的redux
react-redux提供了了两个api
- Provider 为后代组件提供store
- connect 为组件提供数据和变更更⽅方法
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {Provider} from "react-redux";
import store from "./store";
ReactDOM.render(
// 全局提供store
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
// pages/ReduxDemo.js
import * as React from 'react'
import {connect} from 'react-redux'
class ReduxDemo extends React.Component{
constructor(props) {
super(props);
const {counter} = this.props
console.log(counter)
}
render() {
const {counter, add} = this.props
return (
<div>
<p>{ counter }</p>
<button onClick={add}>ADD</button>
</div>
)
}
}
export default connect(
//mapStateToProps 把state映射到props --- Function
//mapDispatchToProps 把dispatch映射到props --- Object
state => {
return {
counter: state
}
},
{
add: () => {
return {type: 'ADD'}
}
}
)(ReduxDemo)
// store/index.js
import {createStore} from "redux";
// 定义state初始化和修改规则
function counterReducer(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + 1
default:
return state
}
}
const store = createStore(counterReducer)
export default store
react-router
react-router中奉⾏行行⼀一切皆组件的思想,路路由器器-Router、链接-Link、路路由-Route、独占-Switch、重定向-Redirect都以组件形式存在
只会渲染一个路径
//App.js
import logo from './logo.svg';
import './App.css';
import ReduxDemo from './pages/ReduxDemo'
import {BrowserRouter, Redirect, Route, Router, Switch} from "react-router-dom";
import RouterPage from "./pages/RouterPage";
import React from "react";
function App({history}) {
return (
<div className="App">
<header className="App-header">
<BrowserRouter>
// switch 当先匹配到 "/" 时,仅会渲染ReduxDemo
// 其实 "/routerPage" 中也有 "/",若用的是Router,则RouterPage也会渲染;switch则不会
<Switch>
<Route exact path="/" component={ReduxDemo} />
<Route path="/routerPage" component={RouterPage} />
// 若都没有匹配上,默认走的路径
<Redirect to="/" />
</Switch>
</BrowserRouter>
</header>
</div>
);
}
export default App;
// RouterPage.js
import {Component} from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom"
import * as React from "react";
import ReduxDemo from "./ReduxDemo";
export default class RouterPage extends Component {
render() {
return (
<div>
<p>RouterPage xxxxxxxxxxxxxxxxxxxxxxxx</p>
<Link to="/redux">ReduxDemo</Link>
</div>
)
}
}
Route渲染内容的三种⽅方式
Route渲染优先级:children>component>render。
这三种⽅方式互斥,你只能用其中一种。
children:func
有时候,不管location是否匹配,你都需要渲染一些内容,这时候你可以用children。
除了不管location是否匹配都会被渲染之外,其它工作方法与render完全一样。
render:func
但是当你用render的时候,你调用的只是个函数。
只在当location匹配的时候渲染。
component: component
只在当location匹配的时候渲染。
PureComponent 与 Component
React.PureComponent 与 React.Component 很相似。两者的区别在于 React.Component 并未实现 shouldComponentUpdate() ,而React.PureComponent 中以浅层对比 prop 和 state 的方式来实现了了该函数。
如果赋予 React 组件相同的 props 和 state, render() 函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent 可提高性能。
注意:
React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。
如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产⽣生错误的比对结果。
仅在你的props 和 state 较为简单时,才使用 React.PureComponent ,或者在深层数据结构发生变化时调用 forceUpdate() 来确保组件被正确地更新。
此外, React.PureComponent 中的 shouldComponentUpdate() 将跳过所有⼦子组件树的 prop
更更新。因此,请确保所有⼦子组件也都是“纯”的组件。
import React, { Component, PureComponent } from "react";
export default class PureComponentPage extends PureComponent {
constructor(props) {
super(props);
this.state = {
counter: 0
};
}
setCounter = () => {
this.setState({
counter: 100
});
}
render() {
const { counter, obj } = this.state;
console.log("render");
return (
<div>
<h1>PuerComponentPage</h1>
// 不需shouldComponentUpdate生命周期函数,也会刷新View
<div onClick={this.setCounter}>counter: {counter}</div>
</div>
)
}
}
Hook
Hook 是什什么? Hook 是⼀一个特殊的函数,它可以让你“钩⼊入” React 的特性。例如, useState 是允许你在 React 函数组件中添加 state 的 Hook。
什么时候我会用 Hook? 如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class。现在你可以在现有的函数组件中使用 Hook。
import React, { useState } from "react";
export default function HookPage(props) {
// 声明⼀一个叫 “count” 的 state 变量量,初始化为0
// setCount: 改变count的值
const [count, setCount] = useState(0);
return (
<div>
<h3>HookPage</h3>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
</div>
);
}
Effect Hook
Effect Hook 可以让你在函数组件中执⾏行行副作⽤用操作。
数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。
// 与生命周期中的 componentDidMount 和 componentDidUpdate 类似
useEffect(() => {
// 更新 title
document.title = `You clicked ${count} times`;
});
- 在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产⽣生莫名其妙的 bug 并破坏 UI 的⼀一致性。
- 使用 useEffect 完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。你可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道。
Effect Hook 的条件执行
默认情况下,effect 会在每轮组件渲染完成后执行。这样的话,一旦 effect 的依赖发生变化,它就会被重新创建。
所以可以给 useEffect 传递第二个参数,它是 effect 所依赖的值数组。
useEffect(() => {
// 只有当 count 发生变化时,才会执行
document.title = `You clicked ${count} times`;
}, [count]);
Effect Hook 的释放
通常,组件卸载时需要清除 effect 创建的诸如订阅或计时器 ID 等资源。要实现这一点, useEffect函数需返回一个清除函数,以防止内存泄漏,清除函数会在组件卸载前执行。
useEffect(() => {
const timer = setInterval(() => {
setDate(new Date());
}, 1000);
// return 返回一个清除函数
return () => clearInterval(timer);
}, []);