const useSetState = (initial) => {
const [set, setSet] = useState(new Set(...initial))
return {
add: el =>
setSet((set) => {
if (set.has(el)) return set
set.add(el)
return new Set(set)
}),
delete: el => {
setSet((set) => {
if (!set.has(el)) return set
set.delete(el)
return new Set(set)
})
},
has: el => set.has(el),
clear: () => setSet(new Set()),
[Symbol.iterator]: () => set.values(),
forEach: (fn) => set.forEach(fn),
keys: () => set.keys(),
values: () => set.values(),
get size() {
return set.size
}
}
}
您可以使用类似const set = useSetState([])的钩子,然后调用set.add(el)、set.delete(el)等。 然而,由于这个钩子的实现方式,返回的对象在那个示例中并不是一个集合,Object.prototype.toString等不会将它识别为一个集合(而且,我没有实现forEach的thisArg)。如果你真的关心这个,你可以使用代理来实现同样的逻辑,但是对于大多数应用程序来说,这似乎有点过分。
function useSet<T>(initialValue?: T[]) {
// a peice of state to trigger a re-render whenever the set changes
// imo this is less hacky than re-creating the set on every state change
const [_, setInc] = useState(false)
//use a ref for the set instead
const set = useRef(new Set<T>(initialValue))
const add = useCallback(
(item: T) => {
if (set.current.has(item)) return
setInc((prev) => !prev)
set.current.add(item)
},
[setInc],
)
const remove = useCallback(
(item: T) => {
if (!set.current.has(item)) return
setInc((prev) => !prev)
set.current.delete(item)
},
[setInc],
)
return [set.current, add, remove] as const
}
8条答案
按热度按时间6kkfgxo01#
根据定义,
Set
是可变的,如果您仅仅调用const newSet = set.add(0)
,React不会触发新的渲染,因为以前的和新的之间的浅层比较将始终Assert为true
您可以使用
spread
运算符在每次更新之间更改引用,但仍保持Set的所有行为添加元素
您仍然可以使用
add
方法,因为它返回更新后的集合正在删除元素
Removing
有点麻烦,首先需要将其转换为数组filter
,然后将结果展开为清楚起见
8zzbczxx2#
我采用了以下方法,并且与我的React Native组件配合得很好。
希望这对有相同用例的人有所帮助。请参考以下Code Sandbox example
b1uwtaje3#
你必须创建一个新的集合,否则react不会知道它需要重新渲染。
e37o9pze4#
如果你想使用一个集合作为你的保存状态(在集合成员的每次改变时重新渲染),你可以把上面描述的技术打包成一个漂亮的小钩子,返回一个行为(几乎...见下文)像集合的对象。
具体来说,我使用以下自定义钩子:
您可以使用类似
const set = useSetState([])
的钩子,然后调用set.add(el)
、set.delete(el)
等。然而,由于这个钩子的实现方式,返回的对象在那个示例中并不是一个集合,Object.prototype.toString等不会将它识别为一个集合(而且,我没有实现forEach的thisArg)。如果你真的关心这个,你可以使用代理来实现同样的逻辑,但是对于大多数应用程序来说,这似乎有点过分。
8fsztsew5#
我决定尝试使用一个对象/数组来代替一个集合,需要注意的是对象的插入顺序是not always preserved,但是如果你有很多动态生成的复选框,并且你收到一个
change
事件:初始化:
增加:
删除:
迭代:
是code sandbox。
xtfmy6hx6#
除了@Peter Gerdes的回答,下面是钩子的TypeScript版本:
b4qexyjb7#
我个人发现,所有这些解决方案都是在每次修改时重新创建集合,这通常会产生相反的效果。我编写了下面的钩子来解决这个问题,IMO使用一个任意状态来触发重新渲染比在每次修改时重新创建集合要简单得多:
这里唯一的缺点是,如果使用者选择绕过提供的
add
和remove
方法,直接修改ref。ttvkxqim8#