Docs

Everything You Need to Know

State objects

Use

State objects provide a powerful interface to your data to make your work easier. When using plain JavaScript objects, you can not judge easily when the data changes. In contrast, the State object automatically renders its bound components when you modify its data.

State objects are easy to create. Just use the new State() class and pass it the data to encapsulate. In the following example we create a State object that represents a JavaScript object of key/value pairs:

// Plain JavaScript object:
const myObject = {
  name: {
    first: 'John',
    last: 'Doe'
  },
  age: 32,
  job: 'web developer'
}

// Create a state:
const personModel = new State(myObject)

To initialize the state, we gave it the object to use. Since it is now a state, we can access its data with accessor methods. These are:

  • personModel.getItemProp()
  • personModel.setItemProp()
  • personModel.deleteItemProp()

To get the name, we would do this:

personModel.getItemProp('name')
// returns: {first: "John", last: "Doe"}
// To get the first name and last name separate:
const firstName = personModel.getItemProp('name').first
// returns: "John"
const lastName = personModel.getItemProp('name').last
// returns: "Doe"

Now let's create a state that holds an array of objects. This is the same as the previous example:

const fruits = [
  {name: "Apple", image: 'images/apple.png'},
  {name: "Banana", image: 'images/banana.png'},
  {name: "Orange", image: 'images/orange.png'},
  {name: "Mango", image: 'images/mango.png'} 
]
const fruitModel = new State(fruits)
const orange = fruits.getItemProp(2, 'name') // returns "Orange"

Because this State object holds an array, we need to provide an index to indicate where in the array we want to get the property from. This is, of course, zero-based.

Extending State

You can extend the State class to add custom functionality. You would do this when you need some state functionality not provided by the default methods. Because State is and ES6 class, you can extend it just like any other class.

The following example is rather trivial, but shows how you can extend State to get a State object with custom features:

class SpecialState extends State {
  constructor(data) {
    if (data) {
      if (Array.isArray(data)) {
        data.map((item, idx) => data[idx] = item.toUpperCase())
      } else {
        data = data.toUpperCase()
      }
    }
    // Pass modified data to super
    super(data)
  }
  pushUpperCase(data) {
    // Add data to State object's "dataStore" property.
    data = data.toUpperCase()
    this.dataStore.push(data)
    // Update any bound components.
    this.renderComponents()
  }
}
const fruitState = new SpecialState(['Apples', 'Oranges', 'Bananas'])

// After creating a custom State object,
// we can use it in a component,
// just like a normal State object:
const fruitList = new Component({
  element: '#fruitList',
  state: fruitState,
  render: (fruit) => html`
    <li>
      <h3>${ fruit }</h3>  
    </li>`
})
// Render the component with its custom State object:
fruitList.render()

With the above custom State class, whatever data you provide during initialization, or pass with the pushUpperCase method will all be upper case.

When Not To Use

Not all data needs to be in a State object. If you have simple data, a string, an array of a couple of items, or a very simple object, it doesn't make sense to put them in a State object. Similarly, if your data is of a temporary nature, don't bother putting it in a State object. If your data is complex and your app depends on it for functionality, put it in a State object. State objects are ideal when you want your components to update automatically when the data changes.

ChocolateChip-UI tries to be efficient at rendering state changes. However, if you are pushing the envelope with data sizes and rapid changes and you see a serious performance degradation, try rendering your component with raw JavaScript data. Another, perhaps better, option is to pause a State object while you are manipulating it and force the bound components to render afterwards. Read the docs for instructions on how to do this.