语法
2024年03月11日
一、Js & Immer
1.1 produce
produce
需要一个 baseState
,以及一个可用于对传入的 draft
进行所有所需更改的 recipe
。关于 Immer
的有趣之处在于 baseState
将保持不变,但 nextState
将反映对 DraftState
所做的所有更改.
语法
produce(baseState, recipe: (draftState) => void): nextState
-
baseState
: 传递给produce
的不可变状态 -
recipe
: 一个可用于对传入的draft
进行所有所需更改的recipe
。在recipe
中,所有标准的JavaScript API
都可以在draft
对象上使用,包括属性字段分配、删除操作和修改数组、Map
和Set
操作,如push
、pop
、splice
、set
、sort
、remove
等。
用法
-
对象
const obj = {
a: 1,
b: {
c: {
e: 2
}
}
}
const objNew = produce(obj, draft => {
draft.b.c.e = 3;
})
console.log(obj)
console.log(objNew); -
数组
const obj = [1,2,3]
const objNew = produce(obj, draft => {
draft[0] = 3;
})
console.log(obj)
console.log(objNew);
1.2 producers
将函数作为第一个参数传递给 produce
会创建一个函数,该函数尚未将 produce
应用于特定 state
,而是创建一个函数,该函数将应用于将来传递给它的任何 state
。这通常称为柯里化。
语法
const baseState = xxx;
function getBaseStateNew (baseState, key,value){
return product(baseState,(draft)=>
xxxx;
);
}
const baseStateNew = getBaseStateNew(baseState,xx,xx);
用法
-
对象
-
数组
const obj = [1,2,3]
function getObjNew(baseState,index,value){
return produce(baseState, (draft)=>{
draft[index] = value;
});
}
const objNew = getObjNew(obj,2,4);
console.log(obj)
console.log(objNew);
一、 Vue & Immer
1.1 useImmer
- App.ts
- useImmer.ts
<template>
<div>
<input
type="text"
:value="obj.b.c.d"
@change="($event) => handleChange($event)"
/>
</div>
</template>
<script setup lang="ts">
import { useImmer } from "./combinatorial/useImmer";
const [obj, setObj] = useImmer({
a: "",
b: {
c: {
d: "",
},
},
});
const handleChange = (event: any) => {
const {
target: { value },
} = event;
setObj((draft: any) => {
console.log(draft)
draft.b.c.d = value;
});
console.log(obj.value.b.c.d)
};
</script>
import { produce } from "immer";
import { shallowRef } from "vue";
export function useImmer(baseState: any) {
const state = shallowRef(baseState);
const update = (updater: any) => {
console.log(state.value)
state.value = produce(state.value, updater);
};
return [state, update];
}
1.2 shallowRef + immer
<template>
<div>
<input type="text" :value="obj.b.c.d" @change="($event)=> handleChange($event)">
</div>
</template>
<script setup lang="ts">
import { produce } from 'immer';
import { shallowRef } from 'vue';
const obj = shallowRef({
a: "",
b: {
c: {
d: "",
},
},
})
const handleChange = (event: any)=>{
const { target: { value} } = event;
const objNew = produce(obj.value,(draft)=>{
draft.b.c.d = value;
});
obj.value = objNew;
console.log(event)
console.log(obj.value.b.c.d)
}
</script>
二、React & Immer
2.1 useImmer
- App.ts
- useImmer.ts
import { useImmer } from "./hooks/useImmer";
function App() {
const [form, setForm] = useImmer({
a: "",
b: {
c: {
d: "",
},
},
});
const handleInputChange = (e: any) => {
const { value } = e.target;
setForm((draft)=>{
draft.b.c.d = value;
});
};
return (
<div>
<input value={form.b.c.d} onChange={handleInputChange}></input>
</div>
);
}
export default App;
import { produce,Draft, freeze } from "immer";
import { useCallback, useState } from "react";
export type DraftFunction<S> = (draft: Draft<S>) => void;
export type Updater<S> = (arg: S | DraftFunction<S>) => void;
export type ImmerHook<S> = [S, Updater<S>];
export type UseImmer = <S = any>(initialValue: S | (() => S)) => ImmerHook<S>;
export const useImmer:UseImmer = (baseState: any) =>{
const [value, updateValue] = useState(() =>
freeze(typeof baseState === "function" ? baseState() : baseState, true)
);
return [
value,
useCallback((updater: any) => {
if (typeof updater === "function") updateValue(produce(updater));
else updateValue(freeze(updater));
}, []),
];
}
2.2 useState + immer
import { useState } from 'react';
import { produce } from "immer";
function App() {
const [form,setForm] = useState({
a: "",
b: {
c: {
d: ""
}
}
})
const handleInputChange = (e: any)=>{
const { value } = e.target;
const formNew = produce(form,(draft)=>{
draft.b.c.d = value;
});
setForm(formNew);
}
return (
<div>
<input value={form.b.c.d} onChange={handleInputChange}></input>
</div>
)
}
export default App
2.3 userImmerReducer
- App.ts
- userImmerReducer.ts
import { useImmerReducer,ImmerReducer } from "./hooks/useImmerReducer";
const initialState = {
a: "",
b: {
c: {
d: "",
},
},
}
const reducer:ImmerReducer<any,any> = (draft, action) => {
switch (action.type) {
case "change":
draft.b.c.d = action.value;
break;
default:
break;
}
}
function App() {
const [form, dispatch] = useImmerReducer(reducer, initialState);
const handleInputChange = (e: any) => {
const { value } = e.target;
dispatch({
type: "change",
value,
});
};
return (
<div>
<input value={form.b.c.d} onChange={handleInputChange}></input>
</div>
);
}
export default App;
import { produce, Draft,nothing } from "immer"
import { useMemo, useReducer } from "react";
export type DraftFunction<S> = (draft: Draft<S>) => void;
export type Updater<S> = (arg: S | DraftFunction<S>) => void;
export type ImmerHook<S> = [S, Updater<S>];
export type ImmerReducer<S, A> = (
draftState: Draft<S>,
action: A
) => void | (S extends undefined ? typeof nothing : S);
export type Reducer<S = any, A = any> = ImmerReducer<S, A>;
export const useImmerReducer = <S,A,I>(reducer: ImmerReducer<S, A>,initializerArg:S & I ,initializer?: (arg: S & I) => S)=>{
const cachedReducer = useMemo(() => produce(reducer), [reducer]);
return useReducer(cachedReducer, initializerArg as any, initializer as any);
}
2.4 useReducer + immer
import { produce } from "immer";
import { useReducer } from "react";
const initialState = {
a: "",
b: {
c: {
d: ""
}
}
}
const reducer = produce((draft,action)=>{
switch(action.type){
case "change":
draft.b.c.d = action.value;
break;
default:
break;
}
})
function App() {
const [ form,dispatch ] = useReducer(reducer,initialState);
const handleInputChange = (e: any) => {
const { value } = e.target;
dispatch({
type: "change",
value
});
};
return (
<div>
<input value={form.b.c.d} onChange={handleInputChange}></input>
</div>
);
}
export default App;