Improve schema performance, especially in repeaters/builders (#18068)

* Cache default child schemas

* safer

* Return current component if the state path is the same

* Add cachedComponentsByStatePath

* Update HasComponents.php
This commit is contained in:
Dan Harrin
2025-10-04 10:41:01 +01:00
committed by GitHub
parent c1e56e5000
commit 396c4efdf7
5 changed files with 42 additions and 16 deletions
@@ -16,6 +16,11 @@ trait HasChildComponents
*/
protected array $childComponents = [];
/**
* @var array<Schema> | null
*/
protected ?array $cachedDefaultChildSchemas = null;
/**
* @param array<Component | Action | ActionGroup | string | Htmlable> | Closure $components
*/
@@ -67,8 +72,8 @@ trait HasChildComponents
*/
public function getChildSchema($key = null): ?Schema
{
if (filled($key) && array_key_exists($key, $containers = $this->getDefaultChildSchemas())) {
return $containers[$key];
if (filled($key) && array_key_exists($key, $this->cachedDefaultChildSchemas ??= $this->getDefaultChildSchemas())) {
return $this->cachedDefaultChildSchemas[$key];
}
$key ??= 'default';
@@ -133,7 +138,7 @@ trait HasChildComponents
}
return [
...(array_key_exists('default', $this->childComponents) ? $this->getDefaultChildSchemas() : []),
...(array_key_exists('default', $this->childComponents) ? ($this->cachedDefaultChildSchemas ??= $this->getDefaultChildSchemas()) : []),
...array_reduce(
array_keys($this->childComponents),
function (array $carry, string $key): array {
@@ -170,6 +175,11 @@ trait HasChildComponents
return ['default' => $this->getChildSchema()];
}
public function clearCachedDefaultChildSchemas(): void
{
$this->cachedDefaultChildSchemas = null;
}
protected function cloneChildComponents(): static
{
foreach ($this->childComponents as $key => $childComponents) {
@@ -552,6 +552,11 @@ trait HasState
data_set($livewire, $this->getStatePath(), $this->evaluate($state));
// For components such as repeaters and builders, the default child schemas depend on the state of the component.
// When loading state into these fields after the state is already present, the cached child schemas need to be
// cleared so that they can be re-evaluated based on the new state. `rawState()` is called during this process.
$this->clearCachedDefaultChildSchemas();
return $this;
}
@@ -16,12 +16,14 @@ class Get
$path = $this->component->resolveRelativeStatePath($path, $isAbsolute);
$component = $this->component->getRootContainer()->getComponentByStatePath(
$path,
withHidden: true,
withAbsoluteStatePath: true,
skipComponentChildContainersWhileSearching: $this->component,
);
$component = ($this->component->getStatePath() === $path)
? $this->component
: $this->component->getRootContainer()->getComponentByStatePath(
$path,
withHidden: true,
withAbsoluteStatePath: true,
skipComponentChildContainersWhileSearching: $this->component,
);
if (! $component) {
return data_get($livewire, $path);
@@ -16,12 +16,14 @@ class Set
$path = $this->component->resolveRelativeStatePath($path, $isAbsolute);
$component = $this->component->getRootContainer()->getComponentByStatePath(
$path,
withHidden: true,
withAbsoluteStatePath: true,
skipComponentChildContainersWhileSearching: $this->component,
);
$component = ($this->component->getStatePath() === $path)
? $this->component
: $this->component->getRootContainer()->getComponentByStatePath(
$path,
withHidden: true,
withAbsoluteStatePath: true,
skipComponentChildContainersWhileSearching: $this->component,
);
$state = $this->component->evaluate($state);
@@ -29,6 +29,11 @@ trait HasComponents
*/
protected ?array $cachedComponents = null;
/**
* @var array<array<array<string, Component>>>
*/
protected array $cachedComponentsByStatePath = [];
/**
* @param array<Component | Action | ActionGroup | string | Htmlable> | Component | Action | ActionGroup | string | Htmlable | Closure $components
*/
@@ -37,6 +42,7 @@ trait HasComponents
$this->components = $components;
$this->cachedComponents = null;
$this->cachedFlatComponents = [];
$this->cachedComponentsByStatePath = [];
return $this;
}
@@ -226,7 +232,7 @@ trait HasComponents
return null;
};
return $search($this);
return $this->cachedComponentsByStatePath[$withHidden][$skipComponentChildContainersWhileSearching ? spl_object_id($skipComponentChildContainersWhileSearching) : null][$statePath] ??= $search($this);
}
/**
@@ -352,6 +358,7 @@ trait HasComponents
$this->cachedComponents = null;
$this->cachedFlatComponents = [];
$this->cachedComponentsByStatePath = [];
}
return $this;