Register to get access to free programming courses with interactive exercises

Complex state JS: Frontend architecture

What does the concept of a state include? In short, the state is the data of our application at any given time, such as open tabs in an editor or browser. The amount of data and their contents change depending on what buttons we press and what we try to load. In general, if there's any visual change in an application or page, then the state will also change, without exception. There can be no case where something on a webpage or application changes, but the state stays the same. The state must change in order for anything shown to change.

You might think that CSS animations won't change anything in our application. Yes, the CSS animation isn't related to our application, but this state is there inside the browser, and it does change.

One great example of a state is forms. Imagine a field that you can put a phone number in, and that field tracks typos and shows them straight away. If there are no errors, it allows you to submit the form, but if there are errors, the button will be grayed out, and you won't be able to send anything. What is the state in this case? Firstly, it's all the data entered into the form. Secondly, it's whether or not the form data is correct or incorrect (valid/invalid). The state is then used to determine whether the field should be surrounded by a red frame or not. And the errors themselves are also part of the state, as they need to be output.

See the Pen js_dom_state_object by Hexlet (@hexlet) on CodePen.

As you can see from the example, the state is described by a typical JS object, which is created when the application is started:

// A constant because the state object always remains the same
const state = {
  // The state of the form is put into a separate property,
  // because there may be multiple forms on the page
  registrationForm: {
    valid: true,
    errors: [],
  }
};

But the logic of the handler has been divided into two independent processes: changing the state, and displaying the state on the screen:

input.addEventListener('keyup', (e) => {
  const inputValue = e.target.value;
  // Saving the input data
  state.registrationForm.value = inputValue;
  // The state of the form and its validity are determined depending on the value entered.
  // If the value is invalid, we mark the form as invalid,
  // otherwise, we mark it as valid
  if (inputValue.match(/^\d+$/)) {
    state.registrationForm.valid = true;
    state.registrationForm.errors = [];
  } else {
    state.registrationForm.valid = false;
    state.registrationForm.errors.push('wrong format');
  }

  // Whether or not the button is blocked/grayed out is determined by the validity state
  submit.disabled = !state.registrationForm.valid;

  // Whether or not there are errors (marked by a red frame) depends on whether the form is valid or not
  if (state.registrationForm.valid) {
    input.style.border = null;
  } else {
    input.style.border = 'thick solid red';
  }
});

This is how all modern frontend frameworks are set up. The very concept of separating application data from the appearance is far from new; moreover, there are simply no other options in backend development (fortunately).

Recommendations for organizing the state

What's stored in the state

Not all data can change over time in applications. Such data may include configuration data such as timeouts, various access keys or library settings. These data are not stored in the state (we can call them static data). It's enough to have separate constants created directly where they're used, or imported from a special module with settings:

// Not great.
const state = {
  // there are all sorts of things
  httpTimeout: 1000,
  apiKey: 'my super secret hash',
};

// Very
const httpTimeout = 1000;
const apiKey = 'my super secret hash';

State object

The state itself is best organized as a single constant containing the object. Such an object is much easier to follow than a set of different constants or variables. What's more, as you'll see later, it allows for easy change tracking to implement the MVC architecture that we're slowly getting to covering.

// Not great.
const state = {
  formState: { /* ... */ },
};

const posts = [];
let connected = false;

// Very
const state = {
  formState: { /* ... */ },
  posts: [],
  connected: false,
};

Naming

Separating the state itself is great, but naming the data inside the state is just as important. What the properties valid or connected in the state object mean is something we can guess, but what they refer to, without analyzing the code, is impossible to understand. These words are too general, they don't give any context. It is always better to specify what you mean:

// not great.
const state = {
  valid: false,
  connected: true,
}

// very
const state = {
  registrationFormValid: false,
  chatConnected: true,
}

Grouping

Another way to make it easier to understand states is to group properties related to the same process. For example, one form can operate with a dozen different properties. If there are several such forms on the page, the state object will grow quickly, and it will be difficult to understand what belongs to what, even if everything has a good name.

// not great.
const state = {
  registrationFormValid: false,
  registrationFormErrors: [],
  registrationFormNameValue: '',
}

// very
const state = {
  registrationForm: {
    valid: false,
    errors: [],
    fields: {
      name: '',
    },
  },
};

Visual structure

A common error when creating a state is tying the structure of the state to the visual design. The problem with this structure is that if you change the design (even a small arrangement of elements), the state object will no longer reflect reality and will have to be corrected.

// Not great.
const state = {
  centralBlock: {
    valid: true,
  },
  sideBar: {
    formValue: 'value'
  },
};

// Very
const state = {
  registrationForm: {
    valid: false,
  },
  searchForm: {
    value: 'value'
  },
};

Storing DOM elements

The state of the application is the data, not how they're displayed. This is why storing DOM elements in the state is considered bad practice. In fact, it is a return to the structure before the introduction of the state when the data were stored inside the view.


Recommended materials

  1. Code Complete: Naming in Programming

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

Bookmate
Health Samurai
Dualboot
ABBYY
Suggested learning programs
profession
Development of front-end components for web applications
10 months
from scratch
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.