diff --git a/packages/forms/src/Components/Concerns/HasState.php b/packages/forms/src/Components/Concerns/HasState.php index 37d8bba409..04358a6b59 100644 --- a/packages/forms/src/Components/Concerns/HasState.php +++ b/packages/forms/src/Components/Concerns/HasState.php @@ -170,7 +170,17 @@ trait HasState { if (! ($isDehydrated && $this->isDehydrated())) { if ($this->hasStatePath()) { - Arr::forget($state, $this->getStatePath()); + $rootContainer = $this->getContainer(); + + while (! $rootContainer->isRoot()) { + $rootContainer = $rootContainer->getParentComponent()->getContainer(); + } + + $statePath = $this->getStatePath(); + + if (! $rootContainer->hasDehydratedComponent($statePath)) { + Arr::forget($state, $statePath); + } return; } diff --git a/packages/forms/src/Concerns/HasState.php b/packages/forms/src/Concerns/HasState.php index acf82a1255..9b670880de 100644 --- a/packages/forms/src/Concerns/HasState.php +++ b/packages/forms/src/Concerns/HasState.php @@ -78,6 +78,27 @@ trait HasState return $state; } + public function hasDehydratedComponent(string $statePath): bool + { + foreach ($this->getComponents(withHidden: true) as $component) { + if (! $component->isDehydrated()) { + continue; + } + + if ($component->hasStatePath() && ($component->getStatePath() === $statePath)) { + return true; + } + + foreach ($component->getChildComponentContainers(withHidden: true) as $container) { + if ($container->hasDehydratedComponent($statePath)) { + return true; + } + } + } + + return false; + } + /** * @param array $state * @return array diff --git a/tests/src/Forms/StateTest.php b/tests/src/Forms/StateTest.php index 70adacd4bc..35b1003085 100644 --- a/tests/src/Forms/StateTest.php +++ b/tests/src/Forms/StateTest.php @@ -486,7 +486,7 @@ test('hidden components are excluded from state dehydration except if they are m ->fill(); expect($container) - ->dehydrateState()->not()->toBe([]); + ->dehydrateState()->data->not()->toBe([]); $container = ComponentContainer::make(Livewire::make()) ->statePath('data') @@ -505,7 +505,7 @@ test('hidden components are excluded from state dehydration except if they are m ->fill(); expect($container) - ->dehydrateState()->not()->toBe([]); + ->dehydrateState()->data->not()->toBe([]); }); test('hidden components are excluded from state dehydration even if their parent component has a state path', function () { @@ -531,6 +531,74 @@ test('hidden components are excluded from state dehydration even if their parent ]); }); +test('components are not excluded from state dehydration if there is another dehydrated field with the same name', function () { + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component) + ->statePath($statePath = Str::random()) + ->default(Str::random()) + ->dehydrated(false), + (new Component) + ->statePath($statePath) + ->default(Str::random()), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->data->not()->toBe([]); + + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component) + ->statePath($statePath = Str::random()) + ->default(Str::random()), + (new Component) + ->statePath($statePath) + ->default(Str::random()) + ->dehydrated(false), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->data->not()->toBe([]); +}); + +test('hidden components are not excluded from state dehydration if there is another visible field with the same name', function () { + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component) + ->statePath($statePath = Str::random()) + ->default(Str::random()) + ->hidden(), + (new Component) + ->statePath($statePath) + ->default(Str::random()), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->data->not()->toBe([]); + + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component) + ->statePath($statePath = Str::random()) + ->default(Str::random()), + (new Component) + ->statePath($statePath) + ->default(Str::random()) + ->hidden(), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->data->not()->toBe([]); +}); + test('disabled components are excluded from state dehydration', function () { $container = ComponentContainer::make(Livewire::make()) ->statePath('data') @@ -577,7 +645,7 @@ test('disabled components are excluded from state dehydration except if they are ->fill(); expect($container) - ->dehydrateState()->not()->toBe([]); + ->dehydrateState()->data->not()->toBe([]); }); test('disabled components are excluded from state dehydration if their parent component is disabled and not marked as dehydrated', function () {