Let’s get some state managed 💃 There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton Well, I guess Phil Karlton never had to deal with managing state on the front end..! State management is one of “ ”. Backends roll their eyes, frontends hide under the desk. After all, managing state is the hardest part of being a frontend developer: you need to think in terms of the UI as something that changes over time. And we are not particularly good at it. those things In this post, we will discover how to from the ground up. We will end up creating our own state manager generator! handle state in a Vue application Let’s dive in: Step 1: Our first app. Election Day! First of all, we need an application. We cannot manage an application state without an application, right? Let’s create a voting app, to let you folks vote for the next President(?): Yes, I merged Single File Components and inline components. TODO (REMOVE BEFORE PUBLISHING): avoid making jokes about politics. Not a good time, not a good time. The code above renders something as pretty as this: It looks like the browser failed to load the CSS I can hear your brain screaming: “ ”. Man, you are not managing state. You are just passing props down to each component. You promised state management. You better deliver Well, isn’t passing props the simplest form of “state management”? Isn’t our main component holding both and , our pieces of state? red blue (The answers are YES and YES) But yeah, I hear you. Passing down props is not pretty nor comfortable nor scalable, so let’s try something else. Step 2: Isolating state Let’s create a “state holder” object and manage our whole state from there. const = {red: 0,blue: 0,} state There it is! Our application state, properly held and encapsulated. It wasn’t that hard! Now, from our components, we could do something like the following: const = {render: h => h('div', `Total votes: ${state.red + state.blue}`)} TotalVotes const = {render: h => h('div', `Red: ${state.red} - Blue: ${state.blue}`),} Results // ...and, inside our main component,...methods: {voteForRed () { state.red++ },voteForBlue () { state.blue++ },}, Spoiler: . Why? this is not going to work Because Vue uses method to trigger its “magic reactivity”. Without passing our data to (heh), Vue won’t be able to track down value changes and update our components in response. data data Easily said, fixed: easily(?) A few things happened there: Look ma’, no props! (lines 8, 9) Every component registers our state in their method. Now Vue is able to track down state changes, so when we vote for 🔴 all our components with the proper value. data rerender (lines 20, 27, 35) We had to remove our pretty arrow function from the render functions because now we are using . this (lines 21, 28) Now our state it “isolated” from components. . Free as in beer (line 14) Ok, so now we have our state separated from our “UI implementation”, but that came with some caveats: in , we cannot use the beautiful arrow functions in our render functions… we need to register our state to each component data() But. Wait. Did I just say “ ”. Vue needs to register data in _data()_ to make it reactive? Yes, I did. But in my solution I’m using every component instance to make the very same data reactive, right? Yes. And could I create a shared Vue instance to hold that reactivity, so my components don’t have to? Well, yes. Let me write a big heading: Step 3: Create a shared Vue instance to hold that reactivity So, information stored in becomes “reactive by default”. And what is the piece of information we want to make reactive? data() Our state! So what if we did this? const state = new Vue({data () {return {red: 0,blue: 0,}},}) Neat! Now our state is reactive. We’ll be sharing a Vue instance for all the data, but that’ll be waaaay cleaner than my previous solution, right? But, wait. Wait. Wait. We have a Vue instance, now. And do you know what a Vue instance can hold, besides reactive data? Exactly: . methods Now our and methods can be with our state! voteforRed() voteForBlue() collocated Let’s check it out: Vuetiful! Let me highlight the improvements we achieved: State and methods that mutate our state are now . No more leaking implementation details! Notice that our voteFor methods are quite simple, but that they could be as complicated as needed. placed together (lines 9, 10) We still need to call these methods from our component. (lines 25, 26) Back to our render functions with arrows. (lines 15, 19) And we removed a lot of boilerplate code (all the declarations). data() Okay, so far so good! Our current solution is terse, simple, and idiomatic. But we need to import Vue, and then create a new instance. While this is not inherently “bad”, I feel we could do better, couldn’t we? For instance, our solution cannot be shared among projects right now. I need to teach people to create a Vue instance, populate its method, then register some methods to modify the state… way too much. data It’s time to… Step 4: Encapsulate our state in a function Fortunately, Javascript provides us with a cool feature that allows us to hide all those details and keep things simple: functions. We are gonna create our . factory function Let’s define our function. What’s the API? I would expect: createStore A parameter to set our initial state. We could call the parameter “state”, for the sake of clarity. data A list of mutations functions to change my state when needed. We could call the parameter “mutations”, for the sake of clarity. Finally, I would expect our to expose a generic method that would allow my components to “run” the mutations. We could call the parameter “commit”, for the sake of clarity (you usually , right?). createStore commit mutations You see where I’m going, don’t ya. We want to end up writing this: const store = createStore({ : { red: 0, blue: 0 }, : {voteForRed (state) { state.red++ },voteForBlue (state) { state.blue++ },},}) state mutations Quite nice, right? And pretty straightforward. Now, how would we implement this helper? Remember that we should use a Vue instance to leverage its reactivity: createStore const = ({ state, mutations }) =>new Vue({data () {return { state }},methods: {commit (mutationName) {mutations[mutationName](this.state)},},}) createStore Some things happened there: First of all, we return a new Vue instance. So far so good. Then, we register our state parameter to the method of the instance. Bam! Our state is now reactive. data() Finally, we create our public method. This method takes a name of a mutation as the parameter, and then runs the very same mutation (and passes our state). If we call , our method will call .Notice that in a real implementation we should handle non-existent mutations! commit() commit('someMutation') mutations.someMutation(this.state) So, how do our component look like, now? const TotalVotes = {render: h => h('div', `Total votes: ${store.state.red + store.state.blue}`),} const Results = {render: h => h('div', `Red: ${store.state.red} - Blue: ${store.state.blue}`),} export default {components: { TotalVotes, Results },methods: {voteForRed () { store.commit('voteForRed') },voteForBlue () { store.commit('voteForBlue') },},} Now we access to get our state, and to modify it (notice that we pass the desired mutation name as parameter). store.state store.commit All together now!: Isn’t that cool? Now we can generate hundreds of thousands of stores by providing a simple method. You’d want to place your in a file and export it, so you can import it in your applications and create a whole new store. Bonus points if you call this file 😁. createStore createStore Vuex.js ✅ That’s a wrap! , … does it sound familiar to you? Well, if you have ever used , it definitely should. We effectively mapped the Vuex API in our example. state mutations Vuex We are missing getters and actions, but I hope you get the idea that Vuex is . It’s a great abstraction, well-polished, useful, scalable. But an abstraction, after all. We just keep adding layers to the heart of the framework: . That’s the core feature that triggers everything. an abstraction of things we already knew reactivity Vuex is . an abstraction of things we already knew A quick recap: State management on the front end is something . My personal recommendation: start as small as possible, and think about it twice before adding new things. Vuex is amazing (it truly is!), but do you really need it yet? scalable is the king of Vue. Everything, and I mean everything, depends on data being reactive. And this is great because we can leverage that reactivity and create nice, useful abstractions. Reactivity Now we understand what Vuex is doing under the hood, which is cool. kinda Sometimes, if it provides context, intent, and repeatability to our code (for instance, step 4 required way more code that step 2). verbosity trumps succinctness Wanna dig in? with 4 commits: one commit per step of the post. Feel free to play with it and inspect every change. I created a Github repo Do you want to practice a bit with our solution? Here’s a challenge: How would you implement ? And ? and… ? 😏 getters actions modules Hope it helps!