How would you completely reset a value in a store in SolidJS
I have something akin to:
interface Item { id: number, price: number, quantity: number }
interface State { items: Array<Item> }
export const ItemsContext = createContext()
export const ContextProvider = (props: any) => {
const [state, setState] = createStore({items: []})
const incrementItemQuantity = ({id}: Item) => {
const index = state.items.findIndex(i => i.id === id)
if(index !== -1) {
const item = state.items[index]
setState("items", index, {quantity: item.quantity + 1})
}
}
const clearItems = () => {
setState(produce(s => s.items = []))
}
const addItem = (item: Item) => {
setState(produce(s => s.items.push(item))
}
const value = [state, { addItem, clearItems, incrementItemQuantity} ]
return (
<ItemsContext.Provider value={value} >
{ props.children }
<ItemsContext.Provider/>
)
}
Adding an item and incrementing its quantity works as expected. When I:
I expect the state to be blank. However, If I add an item with the same properties as the first to the list, it is displayed with the old values.
I can't figure out why. What am I not doing right ?
You are not using the store API correctly. For example, the item path gives you the item, you should get the item first, than update it through a setter:
setState("items", index, {quantity: item.quantity + 1});
Here is how you can do it correctly:
// Here path gives us the item
setState("items", index, item => ({...item, quantity: item.quantity + 1}));
// Here path gives us the property
setState('items', index, 'quantity', q => q + 1);
Here is how you can do it. I did not expose store but items. It is up to you.
// @refresh reload
import { createContext, JSX, useContext } from "solid-js";
import { createStore, produce } from 'solid-js/store';
import { render } from "solid-js/web";
interface Item { id: number, price: number, quantity: number }
interface Store {
items: () => Array<Item>;
add?: (item: Item) => void;
increment?: (index: number) => void;
clear?: () => void;
};
export const CartContext = createContext<Store>();
export const CartProvider = (props: { children: JSX.Element }) => {
const [store, setStore] = createStore({ items: [{ id: 0, price: 10, quantity: 1 }] })
const items = () => store.items;
const add = (item: Item) => setStore('items', items => [...items, item]);
const increment = (index: number) => setStore('items', index, 'quantity', q => q + 1);
const clear = () => setStore('items', []);
return (
<CartContext.Provider value={{ items, add, increment, clear }}>
{props.children}
</CartContext.Provider>
);
}
const Child = () => {
const { items, add, increment, clear } = useContext(CartContext);
return (
<div>
<ul>
{items().map((item, index) => (
<li>{JSON.stringify(item)} <button onclick={() => increment(index)}>inc</button></li>)
)}
</ul>
<div>
<button onClick={() => add({ id: items().length, price: 10, quantity: 1 })}>Add Item</button>
{` `}
<button onClick={() => clear()}>Clear Items</button>
</div>
</div>
)
};
const App = () => {
return (
<CartProvider>
<Child />
</CartProvider>
);
}
render(App, document.querySelector("#app"));
You are not using the store correctly. Check this live example here
import { render } from "solid-js/web";
import { createContext, useContext, For } from "solid-js";
import { createStore } from "solid-js/store";
export const CounterContext = createContext([{ items: [] }, {}]);
export function CounterProvider(props) {
const [state, setState] = createStore({ items: props.items || []});
const store = [
state,
{
add: (val) => setState("items", (c) => [...c, val]),
clear: () => setState("items", () => []),
},
];
return (
<CounterContext.Provider value={store}>
{props.children}
</CounterContext.Provider>
);
}
const Counter = () => {
const [state, { add,clear }] = useContext(CounterContext);
return <>
<For each={state.items}>
{(i) => (<h1>{i}</h1>)}
</For>
<button onClick={() => add(state.items.length + 1)}>Add </button>
<button onClick={clear}>Clear </button>
</>
};
const App = () => (
<CounterProvider>
<Counter />
</CounterProvider>
);
render(() => <App />, document.getElementById("app")!);