state状态
- react中,重要的是组件在UI树中的位置,而不是在JSX中的位置
export default function App(){
if(isFancy){
return(
<Counter isFancy={true}
)
}else{
return(
<Counter isFancy={false}
)
}
}
isFancy值变化,不会重置Counter组件及其state,因为修改前后Counter标签都渲染在相同位置,react会认为是同一个组件
- 如果相同位置渲染不同组件,组件会被移除,连同子树都会重置
- 如果需要重置state,可以给组件key值
useReducer
复杂组件,可能需要增加/修改/删除操作同一个state,可以把状态修改逻辑统一到一个reducer函数中
function stateReducer(state, action){
switch(action.type){
case 'Add':
...
case 'Delete':
...
default: return;
}
}
---------
const [state, dispatch] = useReducer(stateReducer, initialState);
function handleChange(){
dispatch({
type:'Add',
id:...,
text:...
})
}
状态
围绕状态(不同时刻的数据Model,要展示的内容View),组织展示的逻辑
管理
- 软件工程的本质 - 管理数据 在数据基础上做处理,数据存哪里,如何展示
- 淘宝:商家数据的展示,订单数据
设计一个系统,
- 有哪些数据
- 数据的生命周期
- 数据的作用范围
- 数据库(database):可以一直被看到(聊天内容)
- localStorage:搜索记录
- cookie/sessionStorage
- Project runtime(工程运行时/内存):刷新后会清空
- component:state,props
状态管理
一般所说的vuex,redux,mobx都属于project runtime范畴,刷新后,js runtime重新执行,数据就会清空
- 项目运行时,一直全局存在的数据,存在哪里
- 核心:不被GC - Window/Global,闭包
-
组件之外,全局共享数据状态
-
修改数据,相关组件可以获取到
数据改变的时候,执行某个函数
- 发布订阅 - redux
- proxy - mobx
- 修改状态,触发UI更新
实现一个发布订阅
- 2个组件,Child2修改数据,Child1展示数据
const Child1 = () => {
const [count, setCount] = useState(0);
useEffect(() => {
dataObj.subscribe(() => {
// data改变,这里会执行
setCount(dataObj.getData())
})
})
return <div>
<h5>
Child1中 count为{count}
</h5>
</div>
}
const Child2 = () => {
return <div>
<button onClick={() => dataObj.changeData(Math.random())}>random</button>
</div>
}
- 闭包实现
export const createData = function (initData) {
let deps = [];
let data = initData;
function subscribe(handler) {
// 订阅data 的函数 handler,在Data改变的时候执行
deps.push(handler);
}
function changeData(val) {
data = val;
deps.forEach(h => h())
}
function getData() {
return data;
}
return { subscribe, changeData, getData }
}
export const dataObj = createData(0)
问题
- 传递数据的时候,changeData传入的值不固定,可能不规范,unsafe
SAFE By Action
Action,一般由type,payload组成
Reducer
本质是一个计算函数,由action和当前state值,计算出新的state
function reducer(data, action) {
switch (action.type) {
case 'ADD':
return { count: data.count + 1 }
case 'CHANGE':
return { count: data.count + action.payload }
default:
return data
}
}
function changeDataByAction(action) {
data = reducer(data, action);
deps.forEach(h => h())
}
即redux原理