I have an application that runs in various states, for example state1, state2, and state3. Certain behavior of the application will depend on the state the application is in when e.g., a button is pressed, and state in turn will change based on button presses, etc.
I am wondering if there is a standard idiom or design pattern for keeping track of state changes in JavaScript. Perhaps my wording is misleading or inaccurate, so I will give an example. I might want to check state like this:
function foo() {
if (currentState === someState) {
doSomething();
}
}
But I would like to keep track of state in a way that I don't need to hardcode a string state in multiple places, i.e., I would like to avoid doing this:
function foo() {
if (currentState === 'state1') {
doSomething();
}
}
The first thing that came to mind is to create an appState object that has a property for each possible state:
var appState = {
state1: 'state1',
state2: 'state2',
state3: 'state3'
}
This way I could change the current app state like:
currentState = appState.state1;
and check state like:
if (currentState === appState.state1) {
...
}
However, something feels off about this solution; perhaps the repetition of property name and value.
I don't have enough experience reading or writing JavaScript to know the common patterns and best practices yet. I am wondering if the situation I have described above has a standard solution/idiom that is followed by the JavaScript community. Through researching this question, I have come across the State design pattern, and this nice post on how to apply the pattern to JavaScript. I have considered going this route, and have not completely discounted it, but that strategy seems like it might be unnecessarily complex for a simple application.
As javascript is quite dynamic, theres never a best solution. Its based on you what you like much or not. I would, to run a different function on an event, do sth like this:
var states={
dark:{
alert:function(){
alert("its night");
},
//more to come
},
bright:{
alert:function(){
alert("its day");
}
}
}//etc
Just put the functions in it, that change on every state, as you dont want to be repetetive. You could set a current state:
var currentstate=states.dark;
Now you can do, somewhere in your code:
currentstate.alert();
This will also work if state is set to bright. To check wich state is the current, you can do:
if(currentstate==states.dark)
But the states object isnt that senseless then in your example.
PS:
To improve the upper structure, you could use OOP to have substates( that behave similar to another state)
states.grey=Object.create(states.dark);
states.grey.alert=function(){
alert("its getting brighter...");
};
Also this will solve your typo problem:
state.dak //error: cannot read property of undefined.
Related
Ok, here's a puzzler. I have a select input and I'm using Zustand for state. I'm seeing inconsistent state and not getting something I suspect.
It looks like this.
const handleSortChange = async (event) => {
// set a state variable
setSort(event.value)
// do a network call using sortBy and other things
// the call is wrong when using sortBy and I'll replace
// this bug with console.log statements below ...
}
There are two values for the select: "one" and "two".
If I console.log this stuff out, I see the problem and the bug. I can't use the state variable inside of this function. It doesn't await, resolve or behave the way I think it will.
So for my select, if I flip between one and two, I get this funny behavior:
const handleSortChange = async (event) => {
// set a state variable
setSort(event.value) // this sets sortBy
console.log(event.value)
console.log(sortBy) // this is the zustand state variable that is in scope
// I expect these would be the same, but they aren't! :O
}
The console.log output looks like this when switching from "one" to "two" on the select input.
two // event.value
one // the in-scope zustand variable sortBy as a read after the set
When switching to "two" on the select, I get the opposite but these variables aren't the same?
one // event.value
two // the set variable sortBy
When switching to "one" on the select. Because something isn't consistent or resolving like I think it is.
I thought that the zustand state variable would be consistent (especially when I add await and eslint is telling me that await does have effect for this function). This isn't an issue for me right now because I can use the parameter for everything I need. But I just feel like I'm missing something big here and I hope that Zustand isn't going to gotcha me when I need to rely on a state change or consistent store somewhere.
This seems to be the same issue and behavior that React has with setState. With setState in React you wouldn't do this even though this is a common trap. The value is not updated immediately, this way of thinking does not work for a concurrent GUI.
https://twitter.com/acemarke/status/1389376376508227592
In the case of Zustand, it might not even have a callback function to fire after set is called. In other words, this isn't going to work at this time.
when using Redux, the store is supposed to be the single source of truth, and to have no redundancy. Suppose part of the store represents people, who have a name and an age. A person class in traditional object-oriented programming might look something like this:
class Person {
constructor(first, last, birthday) {
this.first = first;
this.last = last;
this.birthday = birthday;
get_fullname() { // ... //}
get_age() { // ... //}
}
However, methods aren't allowed on objects in the Redux store. So, where should these "methods" be implemented?
There are two ways:
In reducers.
Not sure where you got the 'no redundancy in the store' rule. It is perfectly valid to have the results of computations to be kept alongside source data in the store (albeit in such simple case as concatenating name and surname that might not be the best approach)
In selectors.
Selectors are functions used to fetch specific subbranches from the store. They can also perform computations on them before returning. See this chapter in redux' documentation for some examples.
This, with some memoization where needed, is I suppose the better solution of the two.
You can compute this kind of state on-demand inside your connect functions with a selector.
function getFullName(state) {
return `${state.first} ${state.last}`;
}
function mapStateToProps(state) {
return {
fullName: getFullName(state)
};
}
connect(mapStateToProps)(MyComponent);
Check out Reselect, a selector library that is designed to work well with Redux.
Technically there's no rule saying that you can't handle it in your reducer, then store the precomputed state inside your store, but you'll have to remember to update it each and every time the dependent properties change.
Generally you'll end up with simpler code if you keep redundant data out of your store and using selectors allows you to write and share the on-demand computations between your components as and when you need them.
I've managed to create a cycle in my bacon-powered application, so I need some advice on how to break it.
Let's say we have several properties which carry some state of the application. This state is supposed to be stored via HTML5 History API (pushState) and thus I combine those properties to an object and execute a pushState on it. However, I also need to cover popstate events, so on every popstate (back button, etc.) I split the state object back into single properties and inject them into their original streams.
Now I've got myself a cycle: since those property streams are the same streams I got my pushState from in the first place, pushState is called again, duplicating states on the stack and making the back button useless.
This is of course my mistake, however, I don't see a good solution apart from splitting each property stream into two and combining them differently in pushState/popState-specific cases. But that seems rather unelegant -- is there any canonical way to avoid this kind of cycles?
The most elegant solution I come up is to check the current state before pushing new one. If they are the same, do nothing:
// state properties
var foo = ...;
var bar = ...;
// write
var history = Bacon.combineAll(makeHistoryState, foo, bar);
history.onValue(function (v) {
if (!_.isEqual(history.state, v)) {
history.pushState(v);
}
});
// read
window.addEventListener("popstate", function(event.state) {
foo.set(extractFoo(event.state));
bar.set(extractBar(event.state));
});
Disclaimer: I didn't test this myself
EDIT: when having global state as single property, which is saved to history API as is; everything simplfies. And you can probably leverage Bacon.Model lens functionality
I'm afraid this question is pretty stupid....
Using the {{current.name}} syntax I can show the name of $scope.current in the view. I set current when I switch from the list view (/mythings) to editing an item (/mythings?id=someId).
Actually this is redundant as I have the information both in the $location and in $scope.current. This redundancy makes it more complicated to understand, so I'd like to get rid of it.
I replaced current by a current item returning function and hoped it would work (like it does in many other cases). But it doesn't, I need to write {{current().name}} everywhere, which I tend to forget.
Maybe I'm doing it all wrong? I'm a beginner here.
Is there a way to make it work? Somehow bless current so it always gets evaluated before use?
Both alternatives discussed have pros and cons:
Using a property (current) is easier (and more natural) to reference in the view, but it needs to be manually kept in sync with the location.
Using a function (current()) takes care of the keeping in sync issue, but is less intuitive.
All things considered, I would value the auto-syncing feature higher and go for the second alternative (current() function).
But, what if we could get a third option that combines the best of both worlds ?
And in fact we can :)
We can use the concept of Object Properties, introduced by ECMAScript 5, and define some "computed properties" for our $scope (or service?).
Without getting into much detail (see the docs for more details), we could augment a scope like this:
.cotroller('someCtrl', function ($location, $scope) {
Object.defineProperty($scope, 'current', {
get: function () {
return {
name: $location.path();
id: $location.search('id');
};
}
});
Now, we can access $scope.current.name and $scope.current.id as if current were a normal property of $scope (intuitiveness !) and current will be automatically computed based on the current location (auto-sync !).
Thank you ECMAScript 5 :)
This article provides a simple and clear introduction to the concept.
Encapsulation is the core tenent of OO programming.
However, if you make a function private and then you return it so that you can use it, does this to anything effectively.
I would think not, that b.c. if you return a function , you are not returning a copy of it...you are returning a reference. That is the core of the question. When you return a function is it a copy or is it a reference to the actual function.
Does the code below keep func1 private and safe as good practice OO programming would like.
Or does encapsulation / data hiding not really pertain to functions?
var A = (function(){
var func1 = function(param){
// do stuff
},
publik;
publik.retFunc(){
return func1;
}
return publik;
}())
A.retFunc()(arg1);
I'm really not sure where you're going with this...
But, to further the cause:
var Class = (function () {
var private_data = 1234,
private_method = function (x) { private_data += x; },
public_method = function (x) { private_method(x); },
other_method = function () { return private_data; },
public_interface = {
add : public_method,
toString : other_method
};
return public_interface;
}());
I have now programmed to an interface.
This particular interface would be .add and .toString.
The private values are safe from tampering, as they've been enclosed.
add has the ability to access private_method so long as add isn't modified.
See, if you try to do something like this, after the fact:
Class.add = function (x) { steal(private_data + x); };
It's not going to work.
The new function doesn't have a reference to the private data.
So while an external person or program might tamper with the public interface, the internal state is still fine.
Your program will likely still break if it's been tampered with, or other, less protected classes might get compromised, but this one will sit happily, and any internal calls it needs to make (like if it updated a screen, on a timer), will still happen perfectly.
The other point of encapsulation is to choose the interface that you want to present to people.
You could have 30 helper functions inside of a class, but you probably only want to give the external application access to a few of them.
And those public methods will have access to the private data/methods, and the ability to do whatever it is you want the clients to be able to do, and nothing more.
That's an Application Programming Interface.
If I wanted to have a BlogManager class, it might be huge.
Maybe I want it to be able to get stuff from the database, to sort, to set up templates, or to communicate with a view... I want it to be able to filter, I want it to do all kinds of stuff...
But I don't want the end-user to do all of that.
What I want the end user to do is .request(options); or .create(blog_post); or .update(blog_post); or .delete(blog_post);.
If I give the end-user those four methods then nobody can touch the dozens of other things going on inside of the BlogManager to make everything work as expected.
That's programming to an interface.
In the future, when I figure out a better way to filter my results, or when I change my database, or when I change the structure of the data-storage, it isn't going to matter what I do on the inside of my class, because the outside will still look and act the same.
If it has the same public methods, the same input-types, the same return-types... ...then you can do anything you want inside.
There aren't a lot of immediate cases for returning the actual constructor-function, instead of an instated object, though.
Much like there aren't a lot of cases for returning the function, instead of the function's return-value.
Aside from asynchronous programming.