通过 state 管理状态,使用 setState 更新状态,从而更新UI
export default class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0,
otherValue: 0
};
}
handleAdd() {
this.setState((state, props) => ({
...state,
value: ++state.value
}));
}
render() {
return (
<div>
<p>{this.state.value}</p>
<button onClick={() => this.handleAdd}>Add</button>
</div>
);
}
}
react自身的状态管理方案缺点:即使只更新state中某个值,也需要带上其他值(otherValue),操作繁琐
React16.8中正式增加了hooks。通过hooks管理组件内状态,简单易用可扩展。
动机:
import React, { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Add</button>
</div>
);
}
export default Counter;
对比 Class 组件中,使用react自身的状态管理方法,React Hooks使用useState方法在 Function 组件中创建状态变量、更新状态的方法、赋值初始状态。这样就实现了一个拥有自己的状态的 Function 组件。
react自身的数据流管理方案有三种:
父传子:通过props为子组件添加属性数据,即可实现父组件向子组件通信
实现步骤
父组件提供要传递的数据 - state
给子组件标签添加属性
值为 state中的数据
子组件中通过 props
接收父组件中传过来的数据
import React from 'react'
// 函数式子组件
function FSon(props) {
console.log(props)
return (
<div>
子组件1
{props.msg}
</div>
)
}
// 类子组件
class CSon extends React.Component {
render() {
return (
<div>
子组件2
{this.props.msg}
</div>
)
}
}
// 父组件
class App extends React.Component {
state = {
message: 'this is message'
}
render() {
return (
<div>
<div>父组件</div>
<FSon msg={this.state.message} />
<CSon msg={this.state.message} />
</div>
)
}
}
export default App
props说明:
子传父:父组件给子组件传递回调函数,子组件调用
实现步骤
import React from 'react'
// 子组件
function Son(props) {
function handleClick() {
// 调用父组件传递过来的回调函数 并注入参数
props.changeMsg('this is newMessage')
}
return (
<div>
{props.msg}
<button onClick={handleClick}>change</button>
</div>
)
}
class App extends React.Component {
state = {
message: 'this is message'
}
// 提供回调函数
changeMessage = (newMsg) => {
console.log('子组件传过来的数据:',newMsg)
this.setState({
message: newMsg
})
}
render() {
return (
<div>
<div>父组件</div>
<Son
msg={this.state.message}
// 传递给子组件
changeMsg={this.changeMessage}
/>
</div>
)
}
}
export default App
兄弟组件:通过状态提升机制,利用共同的父组件实现兄弟通信
状态提升
状态提升:即把需要通信的 state 提升到两者共同的父组件,实现共享和 Reaction
实现内容:A,B 组件下的 A1,B1 要实现 state 通信
状态提升流程:在 A,B 之上增加一个 Container 组件,并把 A1,B1 需要共享的状态提升,定义到 Container,通过 props 传递 state 以及 changeState 的方法。
此方式存在的一个问题是:以后如果有一个 C 组件的 state,与 A 要做通信,就会再添加一个 Container 组件,如果是 A 的 state 要跟 C 共享,更是毁灭性打击,之前提升到 Container 的 state,还要再提升一层。这种无休止的状态提升问题,后期的通信成本非常高,几乎是重写。
实现步骤
import React from 'react'
// 子组件A
function SonA(props) {
return (
<div>
SonA
{props.msg}
</div>
)
}
// 子组件B
function SonB(props) {
return (
<div>
SonB
<button onClick={() => props.changeMsg('new message')}>changeMsg</button>
</div>
)
}
// 父组件
class App extends React.Component {
// 父组件提供状态数据
state = {
message: 'this is message'
}
// 父组件提供修改数据的方法
changeMsg = (newMsg) => {
this.setState({
message: newMsg
})
}
render() {
return (
<>
{/* 接收数据的组件 */}
<SonA msg={this.state.message} />
{/* 修改数据的组件 */}
<SonB changeMsg={this.changeMsg} />
</>
)
}
}
export default App
跨组件Context:Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法
实现步骤
1- 创建Context对象 导出 Provider 和 Consumer对象
const { Provider, Consumer } = createContext()
2- 使用Provider包裹上层组件提供数据
<Provider value={this.state.message}>
{/* 根组件 */}
</Provider>
3- 需要用到数据的组件使用Consumer包裹获取数据
<Consumer >
{value => /* 基于 context 值进行渲染*/}
</Consumer>
完整代码
import React, { createContext } from 'react'
// 1. 创建Context对象
const { Provider, Consumer } = createContext()
// 3. 消费数据
function ComC() {
return (
<Consumer >
{value => <div>{value}</div>}
</Consumer>
)
}
function ComA() {
return (
<ComC/>
)
}
// 2. 提供数据
class App extends React.Component {
state = {
message: 'this is message'
}
render() {
return (
<Provider value={this.state.message}>
<div className="app">
<ComA />
</div>
</Provider>
)
}
}
export default App
问题:
简单来说来说,“状态管理” 就是为了解决组件间的 “跨级” 通信。
Flux是一套架构模式,而不是代码框架。
【单向数据流】:Action -> Dispatcher -> Store -> View页面交互数据流,如用户点击按钮:View -> Create Action -> Dispatcher(由此进入【单向数据流】)
四大核心部分
Redux是进化Flux,它是在Flux架构模式指导下生成的代码框架,也进一步进行架构约束设计
1、数据来源的唯一性:在redux中所有的数据都是存放在store中,store是单一不可变状态树,这样做的目的就是保证数据来源的唯一性。单个状态树也使调试或检查应用程序变得更容易。
2、state只能是只读的状态: state只能是只读的,State 只能通过触发 Action 来更改,在action中可以去取值,但是不能够去改变它,这个时候采取的方式通常是深度拷贝state,并且将其返回给一个变量,然后改变这个变量,最后将值返回出去。而且要去改变数据只能够在的reducer中,reducer是一个描述了对象发生了一个什么样过程的函数过程。 只读状态的好处,确保视图和网络回调都不会直接写入状态。
3、使用纯函数进行改变:reducer的实质其实就是一个纯函数。每次更改总是返回一个新的 State
核心步骤
<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
<script>
// 定义reducer函数
// 内部主要的工作是根据不同的action 返回不同的state
function counterReducer (state = { count: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 }
case 'DECREMENT':
return { count: state.count - 1 }
default:
return state
}
}
// 使用reducer函数生成store实例
const store = Redux.createStore(counterReducer)
// 订阅数据变化
store.subscribe(() => {
console.log(store.getState())
document.getElementById('count').innerText = store.getState().count
})
// 增
const inBtn = document.getElementById('increment')
inBtn.addEventListener('click', () => {
store.dispatch({
type: 'INCREMENT'
})
})
// 减
const dBtn = document.getElementById('decrement')
dBtn.addEventListener('click', () => {
store.dispatch({
type: 'DECREMENT'
})
})
</script>
import { createSlice } from '@reduxjs/toolkit'
import axios from 'axios'
const channelStore = createSlice({
name: 'channel',
initialState: {
channelList: []
},
reducers: {
setChannelList (state, action) {
state.channelList = action.payload
}
}
})
// 创建异步
const { setChannelList } = channelStore.actions
const url = 'http://geek.itheima.net/v1_0/channels'
// 封装一个函数 在函数中return一个新函数 在新函数中封装异步
// 得到数据之后通过dispatch函数 触发修改
const fetchChannelList = () => {
return async (dispatch) => {
const res = await axios.get(url)
dispatch(setChannelList(res.data.data.channels))
}
}
export { fetchChannelList }
const channelReducer = channelStore.reducer
export default channelReducer
import { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { fetchChannelList } from './store/channelStore'
function App () {
// 使用数据
const { channelList } = useSelector(state => state.channel)
useEffect(() => {
dispatch(fetchChannelList())
}, [dispatch])
return (
<div className="App">
<ul>
{channelList.map(task => <li key={task.id}>{task.name}</li>)}
</ul>
</div>
)
}
export default App
当需要处理复杂的应用状态,且 React 本身无法满足时. 比如:
一个可以和React良好配合的集中状态管理工具,和Redux解决的问题相似,都可以独立组件进行集中状态管理
Mobx 提供了一个类似 Vue 的响应式系统,相对 Redux 来说 Mobx 的架构更容易理解。
响应式数据. 首先使用@observable
将数据转换为‘响应式数据’,类似于 Vue 的 data。这些数据在一些上下文(例如 computed,observer 的包装的 React 组件,reaction)中被访问时可以被收集依赖,当这些数据变动时相关的依赖就会被通知. 响应式数据带来的两个优点是 ① 简化数据操作方式(相比 redux 和 setState); ② 精确的数据绑定,只有数据真正变动时,视图才需要渲染,组件依赖的粒度越小,视图就可以更精细地更新
动作改变状态,状态的改变会更新所有受影响的视图
初始化mobx
初始化步骤
import { makeAutoObservable } from 'mobx'
class CounterStore {
count = 0 // 定义数据
constructor() {
makeAutoObservable(this) // 响应式处理
}
// 定义修改数据的方法
addCount = () => {
this.count++
}
}
const counter = new CounterStore()
export default counter
React使用store
实现步骤
// 导入counterStore
import counterStore from './store'
// 导入observer方法
import { observer } from 'mobx-react-lite'
function App() {
return (
<div className="App">
<button onClick={() => counterStore.addCount()}>
{counterStore.count}
</button>
</div>
)
}
// 包裹组件让视图响应数据变化
export default observer(App)
计算属性(衍生状态)
有一些状态根据现有的状态计算(衍生)得到
实现步骤
import { computed, makeAutoObservable } from 'mobx'
class CounterStore {
list = [1, 2, 3, 4, 5, 6]
constructor() {
makeAutoObservable(this, {
filterList: computed
})
}
// 修改原数组
changeList = () => {
this.list.push(7, 8, 9)
}
// 定义计算属性
get filterList () {
return this.list.filter(item => item > 4)
}
}
const counter = new CounterStore()
export default counter
// 导入counterStore
import counterStore from './store'
// 导入observer方法
import { observer } from 'mobx-react-lite'
function App() {
return (
<div className="App">
{/* 原数组 */}
{JSON.stringify(counterStore.list)}
{/* 计算属性 */}
{JSON.stringify(counterStore.filterList)}
<button onClick={() => counterStore.changeList()}>change list</button>
</div>
)
}
// 包裹组件让视图响应数据变化
export default observer(App)
异步数据处理
实现步骤:
// 异步的获取
import { makeAutoObservable } from 'mobx'
import axios from 'axios'
class ChannelStore {
channelList = []
constructor() {
makeAutoObservable(this)
}
// 只要调用这个方法 就可以从后端拿到数据并且存入channelList
setChannelList = async () => {
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
this.channelList = res.data.data.channels
}
}
const channlStore = new ChannelStore()
export default channlStore
import { useEffect } from 'react'
import { useStore } from './store'
import { observer } from 'mobx-react-lite'
function App() {
const { channlStore } = useStore()
// 1. 使用数据渲染组件
// 2. 触发action函数发送异步请求
useEffect(() => {
channlStore.setChannelList()
}, [])
return (
<ul>
{channlStore.channelList.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)
}
// 让组件可以响应数据的变化[也就是数据一变组件重新渲染]
export default observer(App)
模块化
实现步骤
1- 定义task模块
import { makeAutoObservable } from 'mobx'
class TaskStore {
taskList = []
constructor() {
makeAutoObservable(this)
}
addTask () {
this.taskList.push('vue', 'react')
}
}
const task = new TaskStore()
export default task
2- 定义counterStore
import { makeAutoObservable } from 'mobx'
class CounterStore {
count = 0
list = [1, 2, 3, 4, 5, 6]
constructor() {
makeAutoObservable(this)
}
addCount = () => {
this.count++
}
changeList = () => {
this.list.push(7, 8, 9)
}
get filterList () {
return this.list.filter(item => item > 4)
}
}
const counter = new CounterStore()
export default counter
3- 组合模块导出统一方法
import React from 'react'
import counter from './counterStore'
import task from './taskStore'
class RootStore {
constructor() {
this.counterStore = counter
this.taskStore = task
}
}
const rootStore = new RootStore()
// context机制的数据查找链 Provider如果找不到 就找createContext方法执行时传入的参数
const context = React.createContext(rootStore)
const useStore = () => React.useContext(context)
// useStore() => rootStore { counterStore, taskStore }
export { useStore }
4- 组件使用模块中的数据
import { observer } from 'mobx-react-lite'
// 导入方法
import { useStore } from './store'
function App() {
// 得到store
const store = useStore()
return (
<div className="App">
<button onClick={() => store.counterStore.addCount()}>
{store.counterStore.count}
</button>
</div>
)
}
// 包裹组件让视图响应数据变化
export default observer(App)
redux
mobx
总结