Does Svelte store's auto-subscription work in non-component files? - subscription

Just a basic question: Is the $-syntax for stores applicable in non-component JavaScript files?
The doc says:
Any time you have a reference to a store, you can access its value
inside a component by prefixing it with the $ character.
However, this official example seems to use the $-syntax in a derived store which is not a component:
export const elapsed = derived(
time,
$time => Math.round(($time - start) / 1000)
);
Is this a special case for custom stores? Or is it possible because it gets imported into a component?

The answer is no, because only Svelte files will be compiled.
And you are right about the derived store. But this is only to make clear the callback receives the value and not the subscription. You can use other value names as well and you do not need to start with a $.
export const elapsed = derived(
time,
_time => Math.round((_time - start) / 1000)
);

Related

Shouldn't useContext() only be used for low frequent updates? (mobx-react-lite)

Almost all examples (even the official documentation) use mobx-react-light in combination with useContext() hook.
However, React, many articles and blog-posts advice to NOT USE useContext() for middle/high frequent updates. Isn't state something which can update very frequenty?
Should one use the package in combination with the hook or will performance issues occur?
useContext() is only used to get your store value (reference) and this value is not updated frequently, usually you only set your stores once and don't touch it after. When you use actions you only change observable values of your store and not the store itself. So basically Context is only used to pass reference to the store down the tree, after that all the work performed by MobX only.
Example from MobX docs:
import {observer} from 'mobx-react-lite'
import {createContext, useContext} from "react"
const TimerContext = createContext<Timer>()
const TimerView = observer(() => {
// Grab the timer from the context.
const timer = useContext(TimerContext) // See the Timer definition above.
return (
<span>Seconds passed: {timer.secondsPassed}</span>
)
})
ReactDOM.render(
<TimerContext.Provider value={new Timer()}
<TimerView />
</TimerContext.Provider>,
document.body
)
So you pass value of new Timer() to TimerContext.Provider once when you render your app and it will never be changed or updated after that. Even docs says:
Note that we don't recommend ever replacing the value of a Provider with a different one. Using MobX, there should be no need for that, since the observable that is shared can be updated itself.
However, if you don't have SSR or don't test your app, then you don't even need to use Context at all, you can just use global variables/singletons as your stores and it will work perfectly fine.
Example:
// Initialize timer somewhere
export const myTimer = new Timer()
// You can use directly in the same file or import somewhere else
import { myTimer } from './file-with-your-timer'
// No props, `myTimer` is directly consumed from the closure or from another file
const TimerView = observer(() => <span>Seconds passed: {myTimer.secondsPassed}</span>)
ReactDOM.render(<TimerView />, document.body)
Quote from docs:
Using observables directly works very well, but since this typically introduces module state, this pattern might complicate unit testing. Instead, we recommend using React Context instead.
More about best practices with React: https://mobx.js.org/react-integration.html

Create and Read State for thousands of items using Recoil

I've just started using Recoil on a new project and I'm not sure if there is a better way to accomplish this.
My app is an interface to basically edit a JSON file containing an array of objects. It reads the file in, groups the objects based on a specific property into tabs, and then a user can navigate the tabs, see the few hundred values per tab, make changes and then save the changes.
I'm using recoil because it allows me to access the state of each input from anywhere in my app, which makes saving much easier - in theory...
In order to generate State for each object in the JSON file, I've created an component that returns null and I map over the initial array, create the component, which creates Recoil state using an AtomFamily, and then also saves the ID to another piece of Recoil state so I can keep a list of everything.
Question 1 Is these a better way to do this? The null component doesn't feel right, but storing the whole array in a single piece of state causes a re-render of everything on every keypress.
To Save the data, I have a button which calls a function. That function just needs to get the ID's, loop through them, get the state of each one, and push them into an Array. I've done this with a Selector too, but the issue is that I can't call getRecoilValue from a function because of the Rules of Hooks - but if I make the value available to the parent component, it again slows everything right down.
Question 2 I'm pretty sure I'm missing the right way to think about storing state and using hooks, but I haven't found any samples for this particular use case - needing to generate the state up front, and then accessing it all again on Save. Any guidance?
Question 1
Get accustomed to null-rendering components, you almost can't avoid them with Recoil and, more in general, this hooks-first React world ๐Ÿ˜‰
About the useRecoilValue inside a function: you're right, you should leverage useRecoilCallback for that kind of task. With useRecoilCallback you have a central point where you can get and set whatever you want at once. Take a look at this working CodeSandbox where I tried to replicate (the most minimal way) your use-case. The SaveData component (a dedicated component is not necessary, you could just expose the Recoil callback without creating an ad-hoc component) is the following
const SaveData = () => {
const saveData = useRecoilCallback(({ snapshot }) => async () => {
const ids = await snapshot.getPromise(carIds);
for (const carId of ids) {
const car = await snapshot.getPromise(cars(carId));
const carIndex = db.findIndex(({ id }) => id === carId);
db[carIndex] = car;
}
console.log("Data saved, new `db` is");
console.log(JSON.stringify(db, null, 2));
});
return <button onClick={saveData}>Save data</button>;
};
as you can see:
it retrieves all the ids through const ids = await snapshot.getPromise(carIds);
it uses the ids to retrieve all the cars from the atom family const car = await snapshot.getPromise(cars(carId));
All of that in a central point, without hooks and without subscribing the component to atoms updates.
Question 2
There are a few approaches for your use case:
creating empty atoms when the app starts, updating them, and saving them in the end. It's what my CodeSandbox does
doing the same but initializing the atoms through RecoilRoot' initialState prop
being updated by Recoil about every atom change. This is possible with useRecoilTransactionObserver but please, note that it's currently marked as unstable. A new way to do the same will be available soon (I guess) but at the moment it's the only solution
The latter is the "smarter" approach but it really depends on your use case, it's up to you to think if you really want to update the JSON at every atom' update ๐Ÿ˜‰
I hope it helps, let me know if I missed something ๐Ÿ˜Š

Reselect: how to cache data written to a reducer?

I try to reselect and immediately got up the problem: data polling works. Almost every time the same data arrives in the reducer โ€” a component rerender occurs using this data. I try to cache this data with the selector โ€” fails โ€” the rerender still happens. What is wrong with this code?
function getAllTickets(reducer) {
return reducer.get('tickets');
}
export const allTicketsSelector = createSelector([getAllTickets], items => items);
By default, the surface comparison function is used in createSelector. Want to make a deep comparison, use createSelectorCreator and your comparison function. There is an example in the documentation.

"Reselect" VS "Memoize-One" with "React and Redux"

I'm trying to use some kind of memoization in my workflow with React, and I'm searching for the best and most importantly the "easiest" solution to integrate with my workflow that includes React and Redux.
I came across many articles talking about memoization in general and some demonstrate the use of "memoize-one" and brace it as the fastest and easiest to get up and running with, and others don't even mention it and talk about "reselect".
I just want to know which is better and easiest and which should I invest in.
Both libraries return a function which accepts a given numbers of arguments and returns a value:
getA(arg1, arg2, arg3) // Returns a value
The difference lays in what happens under the hoods when the function is called.
memoize-one
collect provided arguments
compare arguments with the ones provided in previous call (===)
arguments are the same: return cached result
arguments are NOT the same: re-evaluate result function and return
reselect
collect provided arguments
run a set of inputSelectors function providing them with the collected arguments
collect inputSelectors return values
compare inputSelectors return values with the ones obtained in previous call (===)
values are the same: return cached result
values are NOT the same: re-evaluate result function and return
Conclusions
memoize-one is a value-based memoize utility: memoization is performed over the value of provided arguments.
reselect adds a further evaluation layer on top of it: memoization is NOT performed over arguments values BUT over the results of a set inputSelectors functions fed with those initial arguments.
It means that reselect selectors are easily composable since each inputSelectors can be another reselect selector.
I haven't used reselect, but memoize-one works great for me when I want to calculate something from props inside render. This is a great pattern for doing an expensive operation, like mapping a large array, on props that may change over time but also may not change on some re-renders. It ensures an expensive operation used in render is re-computed only when the inputs change. It also avoids having to use lifecycle methods like getDerivedStateFromProps (if it can be calculated from props, it probably shouldn't be on state).
import memoize from 'memoize-one'
class Example extends Component {
mapList = memoize(
(list) => list.map(item => ({text: item.text}))
)
render() {
// if this.props.list hasn't changed since the last render
// memoize-one will re-use the last return value from cache
const mappedList = this.mapList(this.props.list)
return (
...
)
}
}
Keep in mind, in most cases, youโ€™ll want to attach the memoized function to a component instance vs. using a static class variable. This prevents multiple instances of a component from resetting each otherโ€™s memoized keys.
react memoization reactjs
I suggest to use reselect, since it was specifically designed to use with React/Redux. memoize-one is more like a general purpose memoization library.
It's really easy to use reselect, it just wraps your selectors:
import { createSelector } from 'reselect';
const shopItemsSelector = state => state.shop.items;
// without reselect
const subtotalSelector = state => {
const items = shopItemsSelector(state);
return items.reduce((acc, item) => acc + item.value, 0);
}
// with reselect
const subtotalSelector = createSelector(
shopItemsSelector, // list the selectors you need
items => items.reduce((acc, item) => acc + item.value, 0) // the last argument is actual selector
)

Accessing Redux Store in a Util file

TLDR: I want to be able to grab the latest Redux State in an external "Util" file. How can I do this?
Say I have a playlist.. and in many different areas of the app, you can "Start" the playlist. So in a "Util" file I have the "startPlaylist" function so I dont have to write the function in numerous places but in only one place.
The problem with this, is that if I make any changes to the playlist while the playlist is running, "playNextPageInPlaylist" function will not receive any updates to the playlist.
What can I do and change so that my function(s) in the Util file will receive the most updated Redux State?
I have startPlaylist function in 7 different areas, and the functions it involves (all in the Util file) are quite complex.. and it wouldn't make sense to copy and paste that in all 7 files.
Thanks for any help
React.Component File 1
import { startPlaylist } from '../util/EntitiesUtil';
start1() {
startPlaylist( store.playlists[0] );
}
React.Component File 2
import { startPlaylist } from '../util/EntitiesUtil';
start2() {
startPlaylist( store.playlists[0] );
}
EntitiesUtil.js
export function startPlaylist( playlistFromStore ) {
// do stuff
playNextPageInPlaylist( playlistFromStore ); // keeps grabbing next page on a timer
}
You got couple of options, the main options as i see it are:
pass the store to the function (bah please don't do that!).
You can write your own middleware that handles certain action types
and can dispatch other actions if needed (you also get a free
access to the ENTIRE store!).
I think the 2nd option is ideal, as you want your util to do stuff that reflect in the store or need stuff from the store. So basically your util wants to be a part of the redux flow!
Well it's not a component so you can't "connect" it but it can (and should be in my opinion) ad middleware that sits between your actions and reducers.
You can read about middlewares here.
I would have provided you an example of your use case but you didn't post any meaningful code.
Edit
A followup to your comment:
Its quite basic.
You have a signature of a function that never changes, just look at
the docs (it uses
currying,
this is another js topic you should learn)
You need to inject it to the store when you create it with
applymiddleware (same as you did with redux-thunk which is a
middleware by itself).
I realy recommend to look at the source code of redux-thunk the whole 11 lines of it.
You can learn a lot from it.
I believe the store has a getState() method available to you.
Import your created store and then call store.getState()
Check out this example from redux's main site:
http://redux.js.org/docs/api/Store.html#example
function select(state) {
return state.some.deep.property
}
let currentValue
function handleChange() {
let previousValue = currentValue
currentValue = select(store.getState())
if (previousValue !== currentValue) {
console.log(
'Some deep nested property changed from',
previousValue,
'to',
currentValue
)
}
}
We faced a similar issue in using corporate ui react library where state creation was delegated to core library. Thus, exporting store as public variable was not an option to us.
However, there is a horrible way of 'public static variable' that will be updated with your root reducer or 'slicing reducers'.
so, you should make some 'store-util.tsx' with 'let utilStore' variable and export some setter (for reducer) and getter (for any utility functions) functions.

Resources