最近接触的都是小程序的项目,所以增长了不少关于小程序的知识点,目前觉得最有用的就是此篇文章所记录的 — 可增删改查列表。所用原理比较简单,就是声明list变量,在需要的地方遍历list渲染所需的内容,通过内部的 push / remove 等方法去改变所渲染内容。下面是所用代码与示例
hoosk.ts
此文件声明、导出所需方法
import { useCallback, useRef, useState } from 'rax';
/**
* 可增删改的动态列表hooks
*/
export default <T>(initialValue: T[]) => {
const counterRef = useRef(-1);
// key 存储器
const keyList = useRef<number[]>([]);
// 内部方法
const setKey = useCallback((index: number) => {
counterRef.current += 1;
keyList.current.splice(index, 0, counterRef.current);
}, []);
const [list, setList] = useState(() => {
(initialValue || []).forEach((_, index) => {
setKey(index);
});
return initialValue || [];
});
const resetList = useCallback((newList: T[] = []) => {
keyList.current = [];
setList(() => {
(newList || []).forEach((_, index) => {
setKey(index);
});
return newList || [];
});
}, []);
const insert = useCallback((index: number, obj: T) => {
setList((l) => {
const temp = [...l];
temp.splice(index, 0, obj);
setKey(index);
return temp;
});
}, []);
const getKey = useCallback((index: number) => keyList.current[index], []);
const getIndex = useCallback(
(key: number) => keyList.current.findIndex((ele) => ele === key),
[],
);
const merge = useCallback((index: number, obj: T[]) => {
setList((l) => {
const temp = [...l];
obj.forEach((_, i) => {
setKey(index + i);
});
temp.splice(index, 0, ...obj);
return temp;
});
}, []);
const replace = useCallback((index: number, obj: T) => {
setList((l) => {
const temp = [...l];
temp[index] = obj;
return temp;
});
}, []);
const remove = useCallback((index: number) => {
setList((l) => {
const temp = [...l];
temp.splice(index, 1);
// remove keys if necessary
try {
keyList.current.splice(index, 1);
} catch (e) {
console.error(e);
}
return temp;
});
}, []);
const move = useCallback((oldIndex: number, newIndex: number) => {
if (oldIndex === newIndex) {
return;
}
setList((l) => {
const newList = [...l];
const temp = newList.filter((_: {}, index: number) => index !== oldIndex);
temp.splice(newIndex, 0, newList[oldIndex]);
// move keys if necessary
try {
const keyTemp = keyList.current.filter((_: {}, index: number) => index !== oldIndex);
keyTemp.splice(newIndex, 0, keyList.current[oldIndex]);
keyList.current = keyTemp;
} catch (e) {
console.error(e);
}
return temp;
});
}, []);
const push = useCallback((obj: T) => {
setList((l) => {
setKey(l.length);
return l.concat([obj]);
});
}, []);
const pop = useCallback(() => {
// remove keys if necessary
try {
keyList.current = keyList.current.slice(0, keyList.current.length - 1);
} catch (e) {
console.error(e);
}
setList((l) => l.slice(0, l.length - 1));
}, []);
const unshift = useCallback((obj: T) => {
setList((l) => {
setKey(0);
return [obj].concat(l);
});
}, []);
const sortForm = useCallback(
(result: unknown[]) =>
result
.map((item, index) => ({ key: index, item })) // add index into obj
.sort((a, b) => getIndex(a.key) - getIndex(b.key)) // sort based on the index of table
.filter((item) => !!item.item) // remove undefined(s)
.map((item) => item.item), // retrive the data
[],
);
const shift = useCallback(() => {
// remove keys if necessary
try {
keyList.current = keyList.current.slice(1, keyList.current.length);
} catch (e) {
console.error(e);
}
setList((l) => l.slice(1, l.length));
}, []);
return {
list,
insert,
merge,
replace,
remove,
getKey,
getIndex,
move,
push,
pop,
unshift,
shift,
sortForm,
resetList
};
};
index.jsx
此文件用来运用、示例方法
import { createElement, useEffect } from 'rax';
import hooks from './hooks';
function Index() {
// 此处的hooks被调用时括号内为空,实际上可以在括号内传入参数,即可成为list的初始值,不传参则list为[]空数组
const { list, push, remove, replace } = hooks();
useEffect(() => {
// 需要在组件第一次加载的时候,根据自己的需要去渲染相应个数的初始态组件,以form为力,首先渲染一个form.item
if (list.length < 1) {
push({name:1});
}
}, [ list, push, remove, replace ]);
function onSubmit(val, error) {
if (error) return;
// 这里是push进入了一个空对象,实际上可以在对象中添加属性,而添加进的属性,可以在list被遍历时添加到each当中,例如上面的 push({name:1});
push({});
}
return (
<Form
idx={list.length}
onSubmit={onSubmit}
>
{
list.map((each, idx) => (
<>
<Form.Item label={`用户名${idx}`} required requiredMessage="Must Input User Name">
<Input name={`username${each?.name}`} placeholder="Please Input User Name" />
</Form.Item>
{/*
此处的替换传入的也是空值,实际上功能等同于清空当前值,若传入值则可以在当前idx下给each传入属性,比如input的默认值等,更多功能可自行想象与实践
*/}
<Button key={`replace${idx}`} model="solid" type="normal" onClick={()=> replace(idx, {})}>
替换
</Button>
{/*
删除比较好理解,传入当前索引即可
*/}
<Button key={`remove${idx}`} model="solid" type="normal" onClick={()=> remove(idx)}>
删除
</Button>
</>
))
}
<Form.Submit type="secondary" model="text" size="small">
添加
</Form.Submit>
</Form>
);
}
具体实现效果因项目保密原因无法展示,类似效果如图