An Introduction to Visualforce View State

Visualforce pages that contain a form component also contain an encrypted, hidden form field that encapsulates the view state of the page. This view state is automatically created, and as its name suggests, it holds the state of the page - state that includes the components, field values and controller state.

This article provides an introduction to view state, some code samples to show the effect on view state, as well as best practices for optimizing view state in order to improve page performance.

What is View State?

Let's start by understanding the need for view state. In the following illustration, a user requests a web page with a simple form on it, and fills out the form and submits it. If the user's input fails the validation rules for the form, the server responds with an error message - the user corrects the error and resubmits it successfully. Behind the scenes the browser is issuing HTTP requests.

Viewstate2.png

The page is initially retrieved with a GET request and form submissions happen via POST requests. These POST requests are also called postbacks since the data on the form is being posted back to the same page. From the user perspective, this is a stateful interaction since the page state is changing based on its previous state. However HTTP is a stateless protocol, which means that the initial GET and the two subsequent POSTs are treated as independent requests for the page. As a result, some other mechanism is needed to persist state information across HTTP requests.

In Visualforce, page state is persisted as a hidden form field that is automatically inserted into a form when the page gets generated. We call this the view state of the page. The view state captures the state of the page -- state of its associated controllers and extensions and the component tree on the page. The view state is posted back along with the other form data, which gives the server enough information to recreate the page state to which new changes can be applied. Please consult this section in Visualforce documentation to understand the order of execution for a Visualforce page.

What is Contained in the View State?

The data in the view state should be sufficient to recreate the state of the page when the postback is received. To do this, it stores the following data:

  • All non-transient data members in the associated controller (either standard or custom) and the controller extensions.
  • Objects that are reachable from a non-transient data member in a controller or controller extension.
  • The component tree for that page, which represents the page's component structure and the associated state, which are the values applied to those components.
  • A small amount of data for Visualforce to do housekeeping.

View state data is encrypted and cannot be viewed with tools like Firebug. The view state inspector described below lets you look at the contents of view state.

Examining the View State

Developer environments have a view state inspector, which lets you view the contents of the view state. This information can help you in optimizing the view state size. Once enabled, it shows up as a tab in Development Mode, as follows.


Viewer.png


The Visualforce Developer Guide contains details on how to use this tool.

View State in Action

In the previous sections, we looked at what is contained in the view state and how the view state inspector lets you view its contents. To make the concepts around view state a little more concrete, this section looks at a few sample pages and their associated view state.

Sample 1

Here we have a page that uses a standard controller with a single field. The associated view state is shown below.

<apex:page standardController="Account">
    <apex:form >
        <apex:pageBlock title="My Content" mode="edit">
            <apex:pageBlockButtons >
                <apex:commandButton action="{!save}" value="Save"/>
            </apex:pageBlockButtons>
            <apex:pageBlockSection title="My Content Section" columns="2">
                <apex:inputField value="{!account.name}"/>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>


Sample1.png

Sample 2

The following listing shows a Visualforce page with a custom controller and a single field. Figure 4 shows the associated view state which contains an account instance since it has been declared as a non transient member variable in our controller. Notice how simple our controller code to update the account is.

<apex:page controller="MyController1" >
     <apex:form >
        <apex:pageBlock title="My Content" mode="edit">
            <apex:pageBlockButtons >
                <apex:commandButton action="{!save}" value="Save"/>
            </apex:pageBlockButtons>
            <apex:pageBlockSection title="My Content Section" columns="2">
                <apex:inputField value="{!account.name}"/>              
            </apex:pageBlockSection>
        </apex:pageBlock>
      </apex:form>
</apex:page>

public with sharing class myController1 {
   public final Account account {get; set;}

   public myController1() {
        account = [select name from Account where id   = :ApexPages.currentPage().getParameters().get('id')];
    }
    public PageReference save() {
        update account;
        return null;
    }
}


Sample2.png

Sample 3

Let's modify the previous example by making the account name and id controller member variables. We will make the account name member transient so that only record Id is being saved in the view state. The page and the controller code is shown below. When this page is posted back, we will need to retrieve the record again, apply the new values and then update the database. This highlights the main benefit of view state - making the job of a developer easier by automatically maintaining state between postbacks.

<apex:page controller="myController" >
     <apex:form >
        <apex:pageBlock title="My Content" mode="edit">
            <apex:pageBlockButtons >
                <apex:commandButton action="{!save}" value="Save"/>
            </apex:pageBlockButtons>
            <apex:pageBlockSection title="My Content Section" columns="2">
                <apex:outputLabel for="aName">Account Name:</apex:outputLabel>
                <apex:inputText value="{!accountName}"/>              
            </apex:pageBlockSection>
        </apex:pageBlock>
      </apex:form>
</apex:page>

public with sharing class myController {

    private final Id accountId ;
    transient public final String accountName {get; set; }
    
    public myController() {
        Account account = [select Id, Name from Account where id = :ApexPages.currentPage().getParameters().get('id')];
        accountId = account.Id ;
        accountName = account.Name ;
    }
    
    public PageReference save() {
        Account myAccount = [select name from Account where id = :accountId];
        myAccount.name = accountName ;
        update myAccount;
        return null;
    }

}


Viewstate-sample3.png

Sample 4

Finally, let's add a new component to the page and observe the effect on view state. We will add a standard pageMessages component - the resulting view state now shows the controller state associated with this component.

<apex:page controller="myController1" >
     <apex:form >
        <apex:pageBlock title="My Content" mode="edit">
            <apex:pageMessages/>
            <apex:pageBlockButtons >
                <apex:commandButton action="{!save}" value="Save"/>
            </apex:pageBlockButtons>
            <apex:pageBlockSection title="My Content Section" columns="2">
                <apex:inputField value="{!account.name}"/>              
            </apex:pageBlockSection>
        </apex:pageBlock>
      </apex:form>
</apex:page>


Sample3.png

Best Practices for Optimizing View State

View state makes your job as a developer easier by automatically handling all the details for state management during postbacks. However, because the view state has to be transferred back and forth between the browser and Force.com, there could be a potential performance impact in terms of increased page load times when the view state becomes large. Visualforce also imposes a limit of 135K on your view state - if the view size exceeds this limit, an exception gets thrown.

Let us look at a few best practices to optimize view state.

Minimize Number of Forms on a Page

Update: With the introduction of Single View State in the Summer '12 release, a Visualforce page manages a single view state, even when the page contains multiple input forms. Though developers should still try and avoid using multiple form elements as recommended in the following section, it no longer has any bearing on the size of the View State for a Visualforce page.

Assume a page contains two forms - form 1 and form 2. Whichever form the user submits and causes a postback, the view state for the page needs to get transferred. To support this, each form on your page will have its own copy of view state. If the associated view state is large, instead of having multiple forms on a page, have a single form and use <apex:actionRegion> to submit portions of the form. This practice will ensure that only a single copy of the view state is associated with that page. In the example below, two forms are used to update Account fields and the related Contact attributes.


// Using two forms
<apex:page controller="MyController">

<apex:form>
   <apex:commandButton action="{!saveAccount}" value="Update Account"/>
    <!--Account attributes available for editing -->

</apex:form>

<apex:form>
    <apex:commandButton action="{!saveContacts}" value="Update Contacts"/>
     <!--Contact attributes available for editing -->
</apex:form>

</apex:page>

These two can be combined into a single form by leveraging the <apex:actionRegion> component.

// Combining into single form and leveraging <apex:actionRegion>
<apex:page controller="MyController">
  <apex:form>
     <apex:commandButton action="{!saveAccount}" value="Update Account"/>
     <!--Account attributes available for editing -->
   
     <apex:actionRegion>
       <apex:commandButton action="{!saveContacts}" value="Update Contacts"/>
     <!--Contact attributes available for editing -->
    </apex:actionRegion>

  </apex:form>
</apex:page>

You can find additional details on <apex:actionRegion> component here.

Declare Variables as Transient to Reduce View State

An instance variable declared as transient is not saved and is not transmitted as part of the view state. If a certain field is needed only for the duration of the page request and does not need to be part of the view state, declare it as transient. See an example below. Some Apex objects are automatically considered transient. For a full list of such objects and other details on transient, please refer to the docs.

<apex:page controller="ExampleController">
  The Current Time is : {!currentDateTime} <br/>
  <apex:form>
    <apex:commandLink value="refresh"/>
  </apex:form>
</apex:page>

public class ExampleController {

    transient DateTime currentDateTime;

    public String getCurrentDateTime() {
        if (currentDateTime == null) currentDateTime = System.now();
        return '' + currentDateTime;
    }
}

Recreate State versus Storing It in View State

View state should ideally contain only work in progress data (the current object being edited, multi-page wizard data, etc.) If you can reconstruct the data during postback, via a SOQL query or a web services call, do that instead of storing it in controller data members.

Use Custom Objects or Custom Settings to Store Large Quantities of Read-Only Data

Assume that your controller needs to call a Web service and parse a large response object. Storing it in view state may not even be an option given the size. Marking it as transient would incur the cost of an additional Web service call and parsing it again. In such instances, you could store the parsed response in a custom object and store just the record id to get to the parsed response. Custom settings provide another mechanism to cache data needed by your controller. Accessing custom settings is faster than access to custom objects since custom settings are part of your application's cache and does not require a database query to retrieve the data. Please consult the online documentation for additional details on Custom Setting.

Refine Your SOQL to Retrieve Only the Data Needed by the Page

Only retrieve (and store) the fields you need and also filter the data to only retrieve data needed by the page.

Refactor Your Pages to Make Its View Stateless

Instead of using apex:commandLink or apex:commandButton components (which need to be inside a apex:form component) to invoke an action, use an apex:outputLink or other non-action method instead and implement the action through an apex:page action attribute - where it makes sense. The following code shows two ways to invoke a controller method called proccessData() - first with a commandLink, and then with an outputLink and auxiliary page.

// On this page we use a commandLink to invoke a method. CommandLink should be inside a 
// form and thus will have an associated view state
<apex:page controller="ExampleController" >
 <b>Example with command link </b>
 <br/><br/>
 <apex:form >   
     <apex:commandLink value="Process Data" action="{!processData}"/>
 </apex:form>
</apex:page>


//This page accomplishes the same without the need for a view state
<apex:page >
     <b> Example -  using outputLink </b>
     <br/> <br/>
     <apex:outputLink value="{!$Page.MyPage2}">Process Data</apex:outputLink>
</apex:page>

// On MyPage2 you can also invoke a method with an action attribute
<apex:page controller="ExampleController" action="{!processData}">
</apex:page>

Consider Doing Your Own State Management in Certain Cases

In certain cases, you may want to bypass the view state mechanism offered by Visualforce and do your own state management. In such cases, use a HTML FORM instead of apex:form. This technique is useful for dealing with Visualforce pages that may have to be served to mobile devices where the view state may be too large for the embedded browsers.

Summary

View state makes your job as a developer easier by automatically managing state during postbacks. The view state is very much under your control - and you can optimize the size of the view state by paying attention to transient variables, the number of forms on a page, using custom settings to store data and so on.

References