diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php
index c3b1f32..9f15d31 100644
--- src/wp-includes/class-wp-customize-manager.php
+++ src/wp-includes/class-wp-customize-manager.php
@@ -659,6 +659,36 @@ final class WP_Customize_Manager {
 	public function set_post_value( $setting_id, $value ) {
 		$this->unsanitized_post_values();
 		$this->_post_values[ $setting_id ] = $value;
+
+		/**
+		 * Announce when a specific setting's unsanitized post value has been set.
+		 *
+		 * Fires when the {@see WP_Customize_Manager::set_post_value()} method is called.
+		 *
+		 * The dynamic portion of the hook name, `$setting_id`, refers to the setting ID.
+		 *
+		 * @since 4.4.0
+		 *
+		 * @param mixed                $value Unsanitized setting post value.
+		 * @param WP_Customize_Manager $this  WP_Customize_Manager instance.
+		 */
+		do_action( "customize_post_value_set_{$setting_id}", $value, $this );
+
+		/**
+		 * Announce when any setting's unsanitized post value has been set.
+		 *
+		 * Fires when the {@see WP_Customize_Manager::set_post_value()} method is called.
+		 *
+		 * This is useful for <code>WP_Customize_Setting</code> instances to watch
+		 * in order to update a cached previewed value.
+		 *
+		 * @since 4.4.0
+		 *
+		 * @param string               $setting_id Setting ID.
+		 * @param mixed                $value      Unsanitized setting post value.
+		 * @param WP_Customize_Manager $this       WP_Customize_Manager instance.
+		 */
+		do_action( 'customize_post_value_set', $setting_id, $value, $this );
 	}
 
 	/**
diff --git src/wp-includes/class-wp-customize-setting.php src/wp-includes/class-wp-customize-setting.php
index 12f76d4..434dec7 100644
--- src/wp-includes/class-wp-customize-setting.php
+++ src/wp-includes/class-wp-customize-setting.php
@@ -82,6 +82,15 @@ class WP_Customize_Setting {
 	protected $id_data = array();
 
 	/**
+	 * Whether or not preview() was called.
+	 *
+	 * @since 4.4.0
+	 * @access protected
+	 * @var bool
+	 */
+	protected $is_previewed = false;
+
+	/**
 	 * Cache of multidimensional values to improve performance.
 	 *
 	 * @since 4.4.0
@@ -191,6 +200,8 @@ class WP_Customize_Setting {
 		}
 
 		if ( ! empty( $this->id_data['keys'] ) ) {
+			// Note the preview-applied flag is cleared at priority 9 to ensure it is cleared before a deferred-preview runs.
+			add_action( "customize_post_value_set_{$this->id}", array( $this, '_clear_aggregated_multidimensional_preview_applied_flag' ), 9 );
 			$this->is_multidimensional_aggregated = true;
 		}
 	}
@@ -245,6 +256,12 @@ class WP_Customize_Setting {
 		if ( ! isset( $this->_previewed_blog_id ) ) {
 			$this->_previewed_blog_id = get_current_blog_id();
 		}
+
+		// Prevent re-previewing an already-previewed setting.
+		if ( $this->is_previewed ) {
+			return true;
+		}
+
 		$id_base = $this->id_data['base'];
 		$is_multidimensional = ! empty( $this->id_data['keys'] );
 		$multidimensional_filter = array( $this, '_multidimensional_preview_filter' );
@@ -273,7 +290,11 @@ class WP_Customize_Setting {
 			$needs_preview = ( $undefined === $value ); // Because the default needs to be supplied.
 		}
 
+		// If the setting does not need previewing now, defer to when it has a value to preview.
 		if ( ! $needs_preview ) {
+			if ( ! has_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) ) ) {
+				add_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) );
+			}
 			return false;
 		}
 
@@ -327,10 +348,29 @@ class WP_Customize_Setting {
 				 */
 				do_action( "customize_preview_{$this->type}", $this );
 		}
+
+		$this->is_previewed = true;
+
 		return true;
 	}
 
 	/**
+	 * Clear out the previewed-applied flag for a multidimensional-aggregated value whenever its post value is updated.
+	 *
+	 * This ensures that the new value will get sanitized and used the next time
+	 * that <code>WP_Customize_Setting::_multidimensional_preview_filter()</code>
+	 * is called for this setting.
+	 *
+	 * @since 4.4.0
+	 * @access private
+	 * @see WP_Customize_Manager::set_post_value()
+	 * @see WP_Customize_Setting::_multidimensional_preview_filter()
+	 */
+	final public function _clear_aggregated_multidimensional_preview_applied_flag() {
+		unset( self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['preview_applied_instances'][ $this->id ] );
+	}
+
+	/**
 	 * Callback function to filter non-multidimensional theme mods and options.
 	 *
 	 * If switch_to_blog() was called after the preview() method, and the current
@@ -369,13 +409,13 @@ class WP_Customize_Setting {
 	 * the first setting previewed will be used to apply the values for the others.
 	 *
 	 * @since 4.4.0
-	 * @access public
+	 * @access private
 	 *
 	 * @see WP_Customize_Setting::$aggregated_multidimensionals
 	 * @param mixed $original Original root value.
 	 * @return mixed New or old value.
 	 */
-	public function _multidimensional_preview_filter( $original ) {
+	final public function _multidimensional_preview_filter( $original ) {
 		if ( ! $this->is_current_blog_previewed() ) {
 			return $original;
 		}
diff --git src/wp-includes/class-wp-customize-widgets.php src/wp-includes/class-wp-customize-widgets.php
index 6ee6942..7639d50 100644
--- src/wp-includes/class-wp-customize-widgets.php
+++ src/wp-includes/class-wp-customize-widgets.php
@@ -1380,7 +1380,7 @@ final class WP_Customize_Widgets {
 		 * in place from WP_Customize_Setting::preview() will use this value
 		 * instead of the default widget instance value (an empty array).
 		 */
-		$this->manager->set_post_value( $setting_id, $instance );
+		$this->manager->set_post_value( $setting_id, $this->sanitize_widget_js_instance( $instance ) );
 
 		// Obtain the widget control with the updated instance in place.
 		ob_start();
diff --git src/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php src/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php
index 2fa0b5c..073423e 100644
--- src/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php
+++ src/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php
@@ -120,15 +120,6 @@ class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting {
 	public $original_nav_menu_term_id;
 
 	/**
-	 * Whether or not preview() was called.
-	 *
-	 * @since 4.3.0
-	 * @access protected
-	 * @var bool
-	 */
-	protected $is_previewed = false;
-
-	/**
 	 * Whether or not update() was called.
 	 *
 	 * @since 4.3.0
diff --git src/wp-includes/customize/class-wp-customize-nav-menu-setting.php src/wp-includes/customize/class-wp-customize-nav-menu-setting.php
index 766099e..5562a8d 100644
--- src/wp-includes/customize/class-wp-customize-nav-menu-setting.php
+++ src/wp-includes/customize/class-wp-customize-nav-menu-setting.php
@@ -89,15 +89,6 @@ class WP_Customize_Nav_Menu_Setting extends WP_Customize_Setting {
 	public $previous_term_id;
 
 	/**
-	 * Whether or not preview() was called.
-	 *
-	 * @since 4.3.0
-	 * @access protected
-	 * @var bool
-	 */
-	protected $is_previewed = false;
-
-	/**
 	 * Whether or not update() was called.
 	 *
 	 * @since 4.3.0
diff --git tests/phpunit/tests/customize/manager.php tests/phpunit/tests/customize/manager.php
index 481fe61..f9034f2 100644
--- tests/phpunit/tests/customize/manager.php
+++ tests/phpunit/tests/customize/manager.php
@@ -32,8 +32,7 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase {
 	function setUp() {
 		parent::setUp();
 		require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' );
-		$GLOBALS['wp_customize'] = new WP_Customize_Manager();
-		$this->manager = $GLOBALS['wp_customize'];
+		$this->manager = $this->instantiate();
 		$this->undefined = new stdClass();
 	}
 
@@ -66,7 +65,7 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase {
 			define( 'DOING_AJAX', true );
 		}
 
-		$manager = $this->instantiate();
+		$manager = $this->manager;
 		$this->assertTrue( $manager->doing_ajax() );
 
 		$_REQUEST['action'] = 'customize_save';
@@ -82,7 +81,7 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase {
 			$this->markTestSkipped( 'Cannot test when DOING_AJAX' );
 		}
 
-		$manager = $this->instantiate();
+		$manager = $this->manager;
 		$this->assertFalse( $manager->doing_ajax() );
 	}
 
@@ -92,7 +91,7 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase {
 	 * @ticket 30988
 	 */
 	function test_unsanitized_post_values() {
-		$manager = $this->instantiate();
+		$manager = $this->manager;
 
 		$customized = array(
 			'foo' => 'bar',
@@ -114,7 +113,7 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase {
 		);
 		$_POST['customized'] = wp_slash( wp_json_encode( $posted_settings ) );
 
-		$manager = $this->instantiate();
+		$manager = $this->manager;
 
 		$manager->add_setting( 'foo', array( 'default' => 'foo_default' ) );
 		$foo_setting = $manager->get_setting( 'foo' );
@@ -127,12 +126,71 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase {
 	}
 
 	/**
+	 * Test WP_Customize_Manager::set_post_value().
+	 *
+	 * @see WP_Customize_Manager::set_post_value()
+	 */
+	function test_set_post_value() {
+		$this->manager->add_setting( 'foo', array(
+			'sanitize_callback' => array( $this, 'sanitize_foo_for_test_set_post_value' ),
+		) );
+		$setting = $this->manager->get_setting( 'foo' );
+
+		$this->assertEmpty( $this->captured_customize_post_value_set_actions );
+		add_action( 'customize_post_value_set', array( $this, 'capture_customize_post_value_set_actions' ), 10, 3 );
+		add_action( 'customize_post_value_set_foo', array( $this, 'capture_customize_post_value_set_actions' ), 10, 2 );
+		$this->manager->set_post_value( $setting->id, '123abc' );
+		$this->assertCount( 2, $this->captured_customize_post_value_set_actions );
+		$this->assertEquals( 'customize_post_value_set_foo', $this->captured_customize_post_value_set_actions[0]['action'] );
+		$this->assertEquals( 'customize_post_value_set', $this->captured_customize_post_value_set_actions[1]['action'] );
+		$this->assertEquals( array( '123abc', $this->manager ), $this->captured_customize_post_value_set_actions[0]['args'] );
+		$this->assertEquals( array( $setting->id, '123abc', $this->manager ), $this->captured_customize_post_value_set_actions[1]['args'] );
+
+		$unsanitized = $this->manager->unsanitized_post_values();
+		$this->assertArrayHasKey( $setting->id, $unsanitized );
+
+		$this->assertEquals( '123abc', $unsanitized[ $setting->id ] );
+		$this->assertEquals( 123, $setting->post_value() );
+	}
+
+	/**
+	 * Sanitize a value for Tests_WP_Customize_Manager::test_set_post_value().
+	 *
+	 * @see Tests_WP_Customize_Manager::test_set_post_value()
+	 *
+	 * @param mixed $value Value.
+	 * @return int Value.
+	 */
+	function sanitize_foo_for_test_set_post_value( $value ) {
+		return intval( $value );
+	}
+
+	/**
+	 * Store data coming from customize_post_value_set action calls.
+	 *
+	 * @see Tests_WP_Customize_Manager::capture_customize_post_value_set_actions()
+	 * @var array
+	 */
+	protected $captured_customize_post_value_set_actions = array();
+
+	/**
+	 * Capture the actions fired when calling WP_Customize_Manager::set_post_value().
+	 *
+	 * @see Tests_WP_Customize_Manager::test_set_post_value()
+	 */
+	function capture_customize_post_value_set_actions() {
+		$action = current_action();
+		$args = func_get_args();
+		$this->captured_customize_post_value_set_actions[] = compact( 'action', 'args' );
+	}
+
+	/**
 	 * Test the WP_Customize_Manager::add_dynamic_settings() method.
 	 *
 	 * @ticket 30936
 	 */
 	function test_add_dynamic_settings() {
-		$manager = $this->instantiate();
+		$manager = $this->manager;
 		$setting_ids = array( 'foo', 'bar' );
 		$manager->add_setting( 'foo', array( 'default' => 'foo_default' ) );
 		$this->assertEmpty( $manager->get_setting( 'bar' ), 'Expected there to not be a bar setting up front.' );
@@ -162,7 +220,7 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase {
 
 		add_action( 'customize_register', array( $this, 'action_customize_register_for_dynamic_settings' ) );
 
-		$manager = $this->instantiate();
+		$manager = $this->manager;
 		$manager->add_setting( 'foo', array( 'default' => 'foo_default' ) );
 
 		$this->assertEmpty( $manager->get_setting( 'bar' ), 'Expected dynamic setting "bar" to not be registered.' );
diff --git tests/phpunit/tests/customize/setting.php tests/phpunit/tests/customize/setting.php
index da789b1..6d46f3b 100644
--- tests/phpunit/tests/customize/setting.php
+++ tests/phpunit/tests/customize/setting.php
@@ -94,9 +94,9 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 	function test_preview_standard_types_non_multidimensional() {
 		$_POST['customized'] = wp_slash( wp_json_encode( $this->post_data_overrides ) );
 
-		// Try non-multidimensional settings
+		// Try non-multidimensional settings.
 		foreach ( $this->standard_type_configs as $type => $type_options ) {
-			// Non-multidimensional: See what effect the preview filter has on a non-existent setting (default value should be seen)
+			// Non-multidimensional: See what effect the preview filter has on a non-existent setting (default value should be seen).
 			$name = "unset_{$type}_without_post_value";
 			$default = "default_value_{$name}";
 			$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
@@ -106,7 +106,7 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 			$this->assertEquals( $default, call_user_func( $type_options['getter'], $name, $this->undefined ), sprintf( 'Expected %s(%s) to return setting default: %s.', $type_options['getter'], $name, $default ) );
 			$this->assertEquals( $default, $setting->value() );
 
-			// Non-multidimensional: See what effect the preview has on an extant setting (default value should not be seen)
+			// Non-multidimensional: See what effect the preview has on an extant setting (default value should not be seen).
 			$name = "set_{$type}_without_post_value";
 			$default = "default_value_{$name}";
 			$initial_value = "initial_value_{$name}";
@@ -115,11 +115,12 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 			$this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name ) );
 			$this->assertEquals( $initial_value, $setting->value() );
 			$this->assertFalse( $setting->preview(), 'Preview should no-op since setting value was extant and no post value was present.' );
-			$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods)
-			$this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods)
+			$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods).
+			$this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods).
 			$this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name ) );
 			$this->assertEquals( $initial_value, $setting->value() );
 
+			// Non-multidimensional: Try updating a value that had a no-op preview.
 			$overridden_value = "overridden_value_$name";
 			call_user_func( $type_options['setter'], $name, $overridden_value );
 			$message = 'Initial value should be overridden because initial preview() was no-op due to setting having existing value and/or post value was absent.';
@@ -127,17 +128,26 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 			$this->assertEquals( $overridden_value, $setting->value(), $message );
 			$this->assertNotEquals( $initial_value, $setting->value(), $message );
 
-			// Non-multidimensional: Test unset setting being overridden by a post value
+			// Non-multidimensional: Ensure that setting a post value *after* preview() is called results in the post value being seen (deferred preview).
+			$post_value = "post_value_for_{$setting->id}_set_after_preview_called";
+			$this->assertEquals( 0, did_action( "customize_post_value_set_{$setting->id}" ) );
+			$this->manager->set_post_value( $setting->id, $post_value );
+			$this->assertEquals( 1, did_action( "customize_post_value_set_{$setting->id}" ) );
+			$this->assertNotEquals( $overridden_value, $setting->value() );
+			$this->assertEquals( $post_value, call_user_func( $type_options['getter'], $name ) );
+			$this->assertEquals( $post_value, $setting->value() );
+
+			// Non-multidimensional: Test unset setting being overridden by a post value.
 			$name = "unset_{$type}_overridden";
 			$default = "default_value_{$name}";
 			$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
 			$this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $name, $this->undefined ) );
 			$this->assertEquals( $default, $setting->value() );
-			$this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // activate post_data
+			$this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // Activate post_data.
 			$this->assertEquals( $this->post_data_overrides[ $name ], call_user_func( $type_options['getter'], $name, $this->undefined ) );
 			$this->assertEquals( $this->post_data_overrides[ $name ], $setting->value() );
 
-			// Non-multidimensional: Test set setting being overridden by a post value
+			// Non-multidimensional: Test set setting being overridden by a post value.
 			$name = "set_{$type}_overridden";
 			$default = "default_value_{$name}";
 			$initial_value = "initial_value_{$name}";
@@ -145,9 +155,9 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 			$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
 			$this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name, $this->undefined ) );
 			$this->assertEquals( $initial_value, $setting->value() );
-			$this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // activate post_data
-			$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods)
-			$this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods)
+			$this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // Activate post_data.
+			$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods).
+			$this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods).
 			$this->assertEquals( $this->post_data_overrides[ $name ], call_user_func( $type_options['getter'], $name, $this->undefined ) );
 			$this->assertEquals( $this->post_data_overrides[ $name ], $setting->value() );
 		}
@@ -155,24 +165,26 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 
 	/**
 	 * Run assertions on multidimensional standard settings.
+	 *
+	 * @see WP_Customize_Setting::preview()
 	 */
 	function test_preview_standard_types_multidimensional() {
 		$_POST['customized'] = wp_slash( wp_json_encode( $this->post_data_overrides ) );
 
 		foreach ( $this->standard_type_configs as $type => $type_options ) {
-			// Multidimensional: See what effect the preview filter has on a non-existent setting (default value should be seen)
+			// Multidimensional: See what effect the preview filter has on a non-existent setting (default value should be seen).
 			$base_name = "unset_{$type}_multi";
 			$name = $base_name . '[foo]';
 			$default = "default_value_{$name}";
 			$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
 			$this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $base_name, $this->undefined ) );
 			$this->assertEquals( $default, $setting->value() );
-			$this->assertTrue( $setting->preview() );
+			$this->assertTrue( $setting->preview(), "Preview for $setting->id should apply because setting is not in DB." );
 			$base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined );
 			$this->assertArrayHasKey( 'foo', $base_value );
 			$this->assertEquals( $default, $base_value['foo'] );
 
-			// Multidimensional: See what effect the preview has on an extant setting (default value should not be seen)
+			// Multidimensional: See what effect the preview has on an extant setting (default value should not be seen) without post value.
 			$base_name = "set_{$type}_multi";
 			$name = $base_name . '[foo]';
 			$default = "default_value_{$name}";
@@ -183,28 +195,35 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 			$base_value = call_user_func( $type_options['getter'], $base_name, array() );
 			$this->assertEquals( $initial_value, $base_value['foo'] );
 			$this->assertEquals( $initial_value, $setting->value() );
-			$setting->preview();
-			$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods)
-			$this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods)
+			$this->assertFalse( $setting->preview(), "Preview for $setting->id should no-op because setting is in DB and post value is absent." );
+			$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods).
+			$this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods).
 			$base_value = call_user_func( $type_options['getter'], $base_name, array() );
 			$this->assertEquals( $initial_value, $base_value['foo'] );
 			$this->assertEquals( $initial_value, $setting->value() );
 
-			// Multidimensional: Test unset setting being overridden by a post value
+			// Multidimensional: Ensure that setting a post value *after* preview() is called results in the post value being seen (deferred preview).
+			$override_value = "post_value_for_{$setting->id}_set_after_preview_called";
+			$this->manager->set_post_value( $setting->id, $override_value );
+			$base_value = call_user_func( $type_options['getter'], $base_name, array() );
+			$this->assertEquals( $override_value, $base_value['foo'] );
+			$this->assertEquals( $override_value, $setting->value() );
+
+			// Multidimensional: Test unset setting being overridden by a post value.
 			$base_name = "unset_{$type}_multi_overridden";
 			$name = $base_name . '[foo]';
 			$default = "default_value_{$name}";
 			$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
 			$this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $base_name, $this->undefined ) );
 			$this->assertEquals( $default, $setting->value() );
-			$setting->preview();
-			$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods)
-			$this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods)
+			$this->assertTrue( $setting->preview(), "Preview for $setting->id should apply because a post value is present." );
+			$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods).
+			$this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods).
 			$base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined );
 			$this->assertArrayHasKey( 'foo', $base_value );
 			$this->assertEquals( $this->post_data_overrides[ $name ], $base_value['foo'] );
 
-			// Multidimemsional: Test set setting being overridden by a post value
+			// Multidimensional: Test set setting being overridden by a post value.
 			$base_name = "set_{$type}_multi_overridden";
 			$name = $base_name . '[foo]';
 			$default = "default_value_{$name}";
@@ -213,20 +232,20 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 			call_user_func( $type_options['setter'], $base_name, $base_initial_value );
 			$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
 			$base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined );
-			$this->arrayHasKey( 'foo', $base_value );
-			$this->arrayHasKey( 'bar', $base_value );
+			$this->assertArrayHasKey( 'foo', $base_value );
+			$this->assertArrayHasKey( 'bar', $base_value );
 			$this->assertEquals( $base_initial_value['foo'], $base_value['foo'] );
 
 			$getter = call_user_func( $type_options['getter'], $base_name, $this->undefined );
 			$this->assertEquals( $base_initial_value['bar'], $getter['bar'] );
 			$this->assertEquals( $initial_value, $setting->value() );
-			$setting->preview();
-			$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods)
-			$this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods)
+			$this->assertTrue( $setting->preview(), "Preview for $setting->id should apply because post value is present." );
+			$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // Only applicable for custom types (not options or theme_mods).
+			$this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // Only applicable for custom types (not options or theme_mods).
 			$base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined );
 			$this->assertArrayHasKey( 'foo', $base_value );
 			$this->assertEquals( $this->post_data_overrides[ $name ], $base_value['foo'] );
-			$this->arrayHasKey( 'bar', call_user_func( $type_options['getter'], $base_name, $this->undefined ) );
+			$this->assertArrayHasKey( 'bar', call_user_func( $type_options['getter'], $base_name, $this->undefined ) );
 
 			$getter = call_user_func( $type_options['getter'], $base_name, $this->undefined );
 			$this->assertEquals( $base_initial_value['bar'], $getter['bar'] );
@@ -272,7 +291,11 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 			$this->custom_type_data_previewed[ $setting->id ] = $previewed_value;
 		}
 	}
-
+	/**
+	 * Run assertions on custom settings.
+	 *
+	 * @see WP_Customize_Setting::preview()
+	 */
 	function test_preview_custom_type() {
 		$type = 'custom_type';
 		$post_data_overrides = array(
@@ -286,63 +309,69 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 
 		add_action( "customize_preview_{$type}", array( $this, 'custom_type_preview' ) );
 
-		// Custom type not existing and no post value override
+		// Custom type not existing and no post value override.
 		$name = "unset_{$type}_without_post_value";
 		$default = "default_value_{$name}";
 		$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
-		// Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need
+		// Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need.
 		add_filter( "customize_value_{$name}", array( $this, 'custom_type_value_filter' ) );
 		$this->assertEquals( $this->undefined, $this->custom_type_getter( $name, $this->undefined ) );
 		$this->assertEquals( $default, $setting->value() );
-		$setting->preview();
+		$this->assertTrue( $setting->preview() );
 		$this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ) );
 		$this->assertEquals( 1, did_action( "customize_preview_{$setting->type}" ) );
 		$this->assertEquals( $this->undefined, $this->custom_type_getter( $name, $this->undefined ) ); // Note: for a non-custom type this is $default
-		$this->assertEquals( $default, $setting->value() ); // should be same as above
+		$this->assertEquals( $default, $setting->value() ); // Should be same as above.
 
-		// Custom type existing and no post value override
+		// Custom type existing and no post value override.
 		$name = "set_{$type}_without_post_value";
 		$default = "default_value_{$name}";
 		$initial_value = "initial_value_{$name}";
 		$this->custom_type_setter( $name, $initial_value );
 		$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
-		// Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need
+		// Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need.
 		add_filter( "customize_value_{$name}", array( $this, 'custom_type_value_filter' ) );
 		$this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) );
 		$this->assertEquals( $initial_value, $setting->value() );
-		$setting->preview();
+		$this->assertFalse( $setting->preview(), "Preview for $setting->id should not apply because existing type without an override." );
 		$this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ), 'Zero preview actions because initial value is set with no incoming post value, so there is no preview to apply.' );
 		$this->assertEquals( 1, did_action( "customize_preview_{$setting->type}" ) );
-		$this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) ); // should be same as above
-		$this->assertEquals( $initial_value, $setting->value() ); // should be same as above
+		$this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) ); // Should be same as above.
+		$this->assertEquals( $initial_value, $setting->value() ); // Should be same as above.
 
-		// Custom type not existing and with a post value override
+		// Custom type deferred preview (setting post value after preview ran).
+		$override_value = "custom_type_value_{$name}_override_deferred_preview";
+		$this->manager->set_post_value( $setting->id, $override_value );
+		$this->assertEquals( $override_value, $this->custom_type_getter( $name, $this->undefined ) ); // Should be same as above.
+		$this->assertEquals( $override_value, $setting->value() ); // Should be same as above.
+
+		// Custom type not existing and with a post value override.
 		$name = "unset_{$type}_with_post_value";
 		$default = "default_value_{$name}";
 		$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
-		// Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need
+		// Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need.
 		add_filter( "customize_value_{$name}", array( $this, 'custom_type_value_filter' ) );
 		$this->assertEquals( $this->undefined, $this->custom_type_getter( $name, $this->undefined ) );
 		$this->assertEquals( $default, $setting->value() );
-		$setting->preview();
+		$this->assertTrue( $setting->preview() );
 		$this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ), 'One preview action now because initial value was not set and/or there is no incoming post value, so there is is a preview to apply.' );
-		$this->assertEquals( 2, did_action( "customize_preview_{$setting->type}" ) );
+		$this->assertEquals( 3, did_action( "customize_preview_{$setting->type}" ) );
 		$this->assertEquals( $post_data_overrides[ $name ], $this->custom_type_getter( $name, $this->undefined ) );
 		$this->assertEquals( $post_data_overrides[ $name ], $setting->value() );
 
-		// Custom type not existing and with a post value override
+		// Custom type not existing and with a post value override.
 		$name = "set_{$type}_with_post_value";
 		$default = "default_value_{$name}";
 		$initial_value = "initial_value_{$name}";
 		$this->custom_type_setter( $name, $initial_value );
 		$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
-		// Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need
+		// Note: #29316 will allow us to have one filter for all settings of a given type, which is what we need.
 		add_filter( "customize_value_{$name}", array( $this, 'custom_type_value_filter' ) );
 		$this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) );
 		$this->assertEquals( $initial_value, $setting->value() );
-		$setting->preview();
+		$this->assertTrue( $setting->preview() );
 		$this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ) );
-		$this->assertEquals( 3, did_action( "customize_preview_{$setting->type}" ) );
+		$this->assertEquals( 4, did_action( "customize_preview_{$setting->type}" ) );
 		$this->assertEquals( $post_data_overrides[ $name ], $this->custom_type_getter( $name, $this->undefined ) );
 		$this->assertEquals( $post_data_overrides[ $name ], $setting->value() );
 
@@ -361,7 +390,7 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 		$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
 		$this->assertEquals( $this->undefined, get_option( $name, $this->undefined ) );
 		$this->assertEquals( $default, $setting->value() );
-		$setting->preview();
+		$this->assertTrue( $setting->preview() );
 		$this->assertEquals( $default, get_option( $name, $this->undefined ), sprintf( 'Expected get_option(%s) to return setting default: %s.', $name, $default ) );
 		$this->assertEquals( $default, $setting->value() );
 	}
@@ -438,7 +467,7 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 		$this->manager->set_post_value( $name, $post_value );
 		$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type' ) );
 		$this->assertFalse( $setting->is_current_blog_previewed() );
-		$setting->preview();
+		$this->assertTrue( $setting->preview() );
 		$this->assertTrue( $setting->is_current_blog_previewed() );
 
 		$this->assertEquals( $post_value, $setting->value() );
@@ -462,7 +491,7 @@ class Tests_WP_Customize_Setting extends WP_UnitTestCase {
 		$this->manager->set_post_value( $name, $post_value );
 		$setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type' ) );
 		$this->assertFalse( $setting->is_current_blog_previewed() );
-		$setting->preview();
+		$this->assertTrue( $setting->preview() );
 		$this->assertTrue( $setting->is_current_blog_previewed() );
 
 		$blog_id = self::factory()->blog->create();
diff --git tests/phpunit/tests/customize/widgets.php tests/phpunit/tests/customize/widgets.php
index 6584341..1ba37bb 100644
--- tests/phpunit/tests/customize/widgets.php
+++ tests/phpunit/tests/customize/widgets.php
@@ -291,4 +291,57 @@ class Tests_WP_Customize_Widgets extends WP_UnitTestCase {
 		$this->assertFalse( $this->manager->widgets->is_panel_active() );
 		$this->assertFalse( $this->manager->get_panel( 'widgets' )->active() );
 	}
+
+	/**
+	 * @ticket 34738
+	 * @see WP_Customize_Widgets::call_widget_update()
+	 */
+	function test_call_widget_update() {
+
+		$widget_number = 2;
+		$widget_id = "search-{$widget_number}";
+		$setting_id = "widget_search[{$widget_number}]";
+		$instance = array(
+			'title' => 'Buscar',
+		);
+
+		$_POST = wp_slash( array(
+			'action' => 'update-widget',
+			'wp_customize' => 'on',
+			'nonce' => wp_create_nonce( 'update-widget' ),
+			'theme' => $this->manager->get_stylesheet(),
+			'customized' => '{}',
+			'widget-search' => array(
+				2 => $instance,
+			),
+			'widget-id' => $widget_id,
+			'id_base' => 'search',
+			'widget-width' => '250',
+			'widget-height' => '200',
+			'widget_number' => strval( $widget_number ),
+			'multi_number' => '',
+			'add_new' => '',
+		) );
+
+		$this->do_customize_boot_actions();
+
+		$this->assertArrayNotHasKey( $setting_id, $this->manager->unsanitized_post_values() );
+		$result = $this->manager->widgets->call_widget_update( $widget_id );
+
+		$this->assertInternalType( 'array', $result );
+		$this->assertArrayHasKey( 'instance', $result );
+		$this->assertArrayHasKey( 'form', $result );
+		$this->assertEquals( $instance, $result['instance'] );
+		$this->assertContains( sprintf( 'value="%s"', esc_attr( $instance['title'] ) ), $result['form'] );
+
+		$post_values = $this->manager->unsanitized_post_values();
+		$this->assertArrayHasKey( $setting_id, $post_values );
+		$post_value = $post_values[ $setting_id ];
+		$this->assertInternalType( 'array', $post_value );
+		$this->assertArrayHasKey( 'title', $post_value );
+		$this->assertArrayHasKey( 'encoded_serialized_instance', $post_value );
+		$this->assertArrayHasKey( 'instance_hash_key', $post_value );
+		$this->assertArrayHasKey( 'is_widget_customizer_js_value', $post_value );
+		$this->assertEquals( $post_value, $this->manager->widgets->sanitize_widget_js_instance( $instance ) );
+	}
 }
