In my store.js, I may have:
const player = writable({name: 'Player A', highScore: 50});
I may need a function in the store file:
const tick = (latestScore) => {
const highScore = player. highScore;
if (latestScore > highScore) {
//set the player's highScore...
}
}
The idea would be that e.g main.js could invoke 'tick' whenever it needed to and pass in a score, which would conditionally update based on a read.
A delightfully simple task...until the the "reading" of the player's highScore. Obvs, binding this value in a visual component works fine. But, short of using OCR to watch my screen, I have not discovered how to extract data from a writable object in code :-)
AFAIK you'd use player.subscribe() and pass it a callback to get notified when the value changed. You could then save that off in a closure variable somewhere to reference from your tick function.
Aaah - ok. Thanks. I did briefly look at that possibility, and was immediately reminded of John McEnroe remonstrating with an umpire :-)
Writable stores also have a method update which can be passed a function that takes the current value of the store and returns what the store's value should be updated to. https://github.com/sveltejs/rfcs/blob/master/text/0002-reactive-stores.md#store-api
Yes, I see how (for UI components) this is exactly how you'd want to go. Most observer implementations work along similar lines; but they usually leave a getter in place for components that are not event-driven to be able to inspect state on an ad-hoc basis. Anyway, I'll see what breaks if I extend the writable and add my own getter.
Something like this is what I meant:
const tick = (latestScore) => {
player.update(({ name, highScore }) => {
if (highScore < latestScore) highScore = latestScore;
return { name, highScore };
});
}
If you do need to get the current value in a component definition at any moment, and player is in the top level of your component scope, then you can use $player. Even in methods in your script tag, this will be the current value of the store.
Thanks. Inside script tags I've found no issues it's nice and easy to get the value the value. What goes on inside teh script blocks is strong and makes Svelte v cool. I have several parts of an app where the logic is not in script tags but in separate files. In those cases needing to 'update' objects before we can get their value is an interesting design decision.
This would not work with readable or derived. In that case, subscribe work for readable, derived, and writable.
function get__store(store) {
let $val
store.subscribe($ => $val = $)()
return $val
}
writing from the future to thank you for get :) it's pretty handy indeed!
I store the currently logged user's ID in a readable store. It's easy to imagine that I need to retrieve this user ID repeatedly application-wide.
I code:
let userId;
userIdStore.subscribe(id => (userId = id));
// do something with userID
It's ugly and even my IDE complains about userID not having been initialized.
I was thinking, I do not need to subscribe to that value, it will never change except when user logs out. Do I actually need a store? "Subscribing" doesn't make sense here.
What are the alternatives? localStorage/sessionStorage. But I do not want to persist the ID, I'd rather keep it in memory. If I persist something, I will do it for a reason, I do not see any strong reason here for me. (user stays logged in through cookies)
Then was thinking, I need an additional store, writable and readable don't answer my needs, I need a store that once sets a value and that value will never change again, where I'd have a "get" instead of a "subscribe". I could write such a custom store myself, but the store contract requires a subscribe method. Maybe I should just use the window object? :-/
@thojanssens
If you are using in .svelte file you can just use $ symbol before the store name ($storeName) to get or set the store value
```
// App.svelte
import { user } from "./store.js"
console.log($user);
// you can also set the store value
$user = {name:"new user"}
```
If you are using in .js file you need to subscribe to the value to get. you cannot use $storeName
If you want to do it
Use .svelte and set the context="module"
This will load the script only one time
but i am not 100% sure if it support the store test it out and let me know
I just need to "subscribe once" to this userID value. The store is not a good fit. I don't know why I was so much focused on using stores, I just coded my own kind of store:
let userId;
function get() { return userId }
async function fetch() {
const query = 'query { me { id } }';
const parsed = await request(query);
userId = parsed.me.id;
return userId;
}
export { get, fetch }
In the root .svelte file:
import { fetch as fetchUserId } from './stores/userId.js';
onMount(async () => {
await fetchUserId();
Now that the user ID has been fetched from the server, I can use get application-wide.
I have to be careful about the onMount order because the children onMount will be called before the parent's onMount, so I still got to figure out how to deal with that flow.
you can import get from svelte/store https://svelte.dev/docs#get
Wouldn't it be nicer if we could do writable.get() instead?
get(writable) is not so great for performance. From the docs:
This works by creating a subscription, reading the value, then unsubscribing. It's therefore not recommended in hot code paths.
I use writable stores _a lot_ on .js files on my Svelte projects. I don't really read values thousands of times per second with get(), per I'm concerned this might not be a valid approach in the long run.
@PierBover That wouldn't work on custom stores. Using the already existing get works on all stores. Why don't you just manually subscribe?
@kevmodrome because I just want to read a value at a particular time and not trigger reactive logic.
Edit:
if (get(writable) === true) {
// etc
}
this makes me sad
import { get } from 'svelte/store';
Can't understand why you can't just use get on the writable.
this makes me sad
import { get } from 'svelte/store';Can't understand why you can't just use get on the writable.
I am looking into this and learning. I understand how get(writable) works, shortcomings etc. but yes: why is this needed if writable stores already have get() method? Perhaps because it could happen that we could possibly read an inconsistent state by calling get() directly? There could exist some custom implementations of the store where one could set state in multiple calls without triggering notifications to subscribers... and if in between such calls someone called writable.get() they would read inconsistent state.
Is this the reason for separate get() function that does automatic subscription, reading and cancelling the subscription?
I use my own store function.
store is a writable that has a get method.
import {writable} from "../web_modules/svelte/store.js";
export function store(value) {
let originalWritable = writable(value);
function set(newValue) {
return originalWritable.set(value = newValue);
}
function update(fn) {
originalWritable.update((oldValue) => value = fn(oldValue));
}
function get() {
return value;
}
return {set, update, subscribe: originalWritable.subscribe, get};
}
Typescript version
import { Readable, Writable, writable } from 'svelte/store';
export type Store<T> = Writable<T> & { get(): T };
export function store<T>(value: T): Store<T> {
let originalWritable = writable<T>(value);
function set(newValue: any) {
return originalWritable.set(value = newValue);
}
function update(fn: (originalValue: T) => T) {
originalWritable.update((oldValue: T) => (value = fn(oldValue)));
}
function get() {
return value;
}
return { set, update, subscribe: originalWritable.subscribe, get }
}
Most helpful comment
writing from the future to thank you for
get:) it's pretty handy indeed!