Repository: livewire/livewire (php)

Issue:
Redirect Not Working in 2.3.6
### Description

I just upgraded to 2.3.6 and my redirect doesn't work. The view does not render and the redirect does not happen.

### Exact steps to reproduce

Create a new laravel project, install livewire, create a component, put `$this->redirect('https://laravel-livewire.com')` in the mount method on the component. Put the component on a page and load the browser.

### Stripped-down, copy-pastable code snippets

```
public function mount()
    {
        $this->redirect('https://google.com');
    }
```

One other thing to note is that I've tried to `dd` in the `PerformsRedirects` class and it never even gets there.

**Context**
- Livewire version: 2.3.6
- Laravel version: 8.22.1
- Alpine version: N/A
- Browser: Chrome


Pull request description:
initialize with an empy div to ensure initial render redirects for ne…
Livewire sill needs a dom element to initially render a component.

Therefore we still skip rendering from the component but add an empty div so livewire can initialize the component properly to then redirect.

---
Fixes #2348 

Patch:
diff --git a/config/livewire.php b/config/livewire.php
index a97fb590..e648b8e3 100644
--- a/config/livewire.php
+++ b/config/livewire.php
@@ -90,6 +90,7 @@ return [
             'mov', 'avi', 'wmv', 'mp3', 'm4a',
             'jpg', 'jpeg', 'mpga', 'webp', 'wma',
         ],
+        'max_upload_time' => 5, // Max duration (in minutes) before an upload gets invalidated.
     ],
 
     /*
diff --git a/src/Component.php b/src/Component.php
index 995119f9..377d0a80 100644
--- a/src/Component.php
+++ b/src/Component.php
@@ -58,7 +58,7 @@ abstract class Component
             'view' => $this->initialLayoutConfiguration['view'] ?? config('livewire.layout', 'layouts.app'),
             'params' => $this->initialLayoutConfiguration['params'] ?? [],
             'slotOrSection' => $this->initialLayoutConfiguration['slotOrSection'] ?? [
-                'extends' => 'content', 'component' => 'default',
+                'extends' => 'content', 'component' => 'slot',
             ][$layoutType],
             'manager' => $manager,
         ]);
diff --git a/src/FileUploadConfiguration.php b/src/FileUploadConfiguration.php
index d613b4eb..510daad9 100644
--- a/src/FileUploadConfiguration.php
+++ b/src/FileUploadConfiguration.php
@@ -66,7 +66,7 @@ class FileUploadConfiguration
         return $prefix.($prefix ? '/' : '').$directory.($path ? '/' : '').$path;
     }
 
-     public static function mimeType($filename)
+    public static function mimeType($filename)
     {
         $mimeType = static::storage()->getMimeType(static::path($filename));
         return $mimeType === 'image/svg' ? 'image/svg+xml' : $mimeType;
@@ -87,4 +87,9 @@ class FileUploadConfiguration
 
         return explode('|', $rules);
     }
+
+    public static function maxUploadTime()
+    {
+        return config('livewire.temporary_file_upload.max_upload_time');
+    }
 }
diff --git a/src/GenerateSignedUploadUrl.php b/src/GenerateSignedUploadUrl.php
index 477aafed..3b8d0991 100644
--- a/src/GenerateSignedUploadUrl.php
+++ b/src/GenerateSignedUploadUrl.php
@@ -9,7 +9,7 @@ class GenerateSignedUploadUrl
     public function forLocal()
     {
         return URL::temporarySignedRoute(
-            'livewire.upload-file', now()->addMinutes(5)
+            'livewire.upload-file', now()->addMinutes(FileUploadConfiguration::maxUploadTime())
         );
     }
 
@@ -32,7 +32,7 @@ class GenerateSignedUploadUrl
 
         $signedRequest = $adapter->getClient()->createPresignedRequest(
             $command,
-            '+5 minutes'
+            '+' . FileUploadConfiguration::maxUploadTime() . ' minutes'
         );
 
         return [
diff --git a/src/Macros/ViewMacros.php b/src/Macros/ViewMacros.php
index c6a2bc94..b6c300ef 100644
--- a/src/Macros/ViewMacros.php
+++ b/src/Macros/ViewMacros.php
@@ -23,7 +23,7 @@ class ViewMacros
         return function ($view, $params = []) {
             $this->livewireLayout = [
                 'type' => 'component',
-                'slotOrSection' => 'default',
+                'slotOrSection' => 'slot',
                 'view' => $view,
                 'params' => $params,
             ];
diff --git a/src/Macros/livewire-view-component.blade.php b/src/Macros/livewire-view-component.blade.php
index fda528f5..0fb6d628 100644
--- a/src/Macros/livewire-view-component.blade.php
+++ b/src/Macros/livewire-view-component.blade.php
@@ -1,3 +1,5 @@
 @component($view, $params)
-    {!! $manager->initialDehydrate()->toInitialResponse()->effects['html']; !!}
+    @slot($slotOrSection)
+        {!! $manager->initialDehydrate()->toInitialResponse()->effects['html']; !!}
+    @endslot
 @endcomponent
diff --git a/src/RenameMe/SupportRedirects.php b/src/RenameMe/SupportRedirects.php
index 25eb3baa..65314c28 100644
--- a/src/RenameMe/SupportRedirects.php
+++ b/src/RenameMe/SupportRedirects.php
@@ -40,6 +40,7 @@ class SupportRedirects
             }
 
             $response->effects['redirect'] = $component->redirectTo;
+            $response->effects['html'] = $response->effects['html'] ?? '<div></div>';
         });
     }
 }


Test patch:
diff --git a/tests/Unit/ComponentLayoutTest.php b/tests/Unit/ComponentLayoutTest.php
new file mode 100644
index 00000000..85f243ec
--- /dev/null
+++ b/tests/Unit/ComponentLayoutTest.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Tests\Unit;
+
+use Illuminate\Support\Facades\Route;
+use Livewire\Component;
+use Livewire\Livewire;
+
+class ComponentLayoutTest extends TestCase
+{
+    /** @test */
+    public function can_extend_a_blade_layout()
+    {
+        Livewire::component(ComponentWithExtendsLayout::class);
+
+        Route::get('/foo', ComponentWithExtendsLayout::class);
+
+        $this->get('/foo')->assertSee('baz');
+    }
+
+    /** @test */
+    public function can_set_custom_section()
+    {
+        Livewire::component(ComponentWithCustomSection::class);
+
+        Route::get('/foo', ComponentWithCustomSection::class);
+
+        $this->get('/foo')->assertSee('baz');
+    }
+
+    /** @test */
+    public function can_set_custom_layout()
+    {
+        Livewire::component(ComponentWithCustomLayout::class);
+
+        Route::get('/foo', ComponentWithCustomLayout::class);
+
+        $this->get('/foo')->assertSee('baz');
+    }
+
+    /** @test */
+    public function can_set_custom_slot_for_a_layout()
+    {
+        Livewire::component(ComponentWithCustomSlotForLayout::class);
+
+        Route::get('/foo', ComponentWithCustomSlotForLayout::class);
+
+        $this->withoutExceptionHandling()->get('/foo')->assertSee('baz');
+    }
+}
+
+class ComponentWithExtendsLayout extends Component
+{
+    public function render()
+    {
+        return view('null-view')->extends('layouts.app-extends', [
+            'bar' => 'baz'
+        ]);
+    }
+}
+
+class ComponentWithCustomSection extends Component
+{
+    public $name = 'baz';
+
+    public function render()
+    {
+        return view('show-name')->extends('layouts.app-custom-section')->section('body');
+    }
+}
+
+class ComponentWithCustomLayout extends Component
+{
+    public function render()
+    {
+        return view('null-view')->layout('layouts.app-with-bar', [
+            'bar' => 'baz'
+        ]);
+    }
+}
+
+class ComponentWithCustomSlotForLayout extends Component
+{
+    public $name = 'baz';
+
+    public function render()
+    {
+        return view('show-name')->layout('layouts.app-custom-slot')->slot('main');
+    }
+}
diff --git a/tests/Unit/ComponentSkipRenderTest.php b/tests/Unit/ComponentSkipRenderTest.php
index 64eaadda..ee84befa 100644
--- a/tests/Unit/ComponentSkipRenderTest.php
+++ b/tests/Unit/ComponentSkipRenderTest.php
@@ -35,7 +35,7 @@ class ComponentSkipRenderTest extends TestCase
         $component = Livewire::test(ComponentSkipRenderOnRedirectInMountStub::class);
 
         $this->assertEquals('/foo', $component->payload['effects']['redirect']);
-        $this->assertNull($component->payload['effects']['html']);
+        $this->assertNull($component->lastRenderedView);
 
         Route::get('/403', ComponentSkipRenderOnRedirectInMountStub::class);
         $this->get('/403')->assertRedirect('/foo');
@@ -43,7 +43,7 @@ class ComponentSkipRenderTest extends TestCase
         $component = Livewire::test(ComponentSkipRenderOnRedirectHelperInMountStub::class);
 
         $this->assertStringEndsWith('/bar', $component->payload['effects']['redirect']);
-        $this->assertNull($component->payload['effects']['html']);
+        $this->assertNull($component->lastRenderedView);
 
         Route::get('/403', ComponentSkipRenderOnRedirectHelperInMountStub::class);
         $this->get('/403')->assertRedirect('/bar');
diff --git a/tests/Unit/views/layouts/app-custom-section.blade.php b/tests/Unit/views/layouts/app-custom-section.blade.php
new file mode 100644
index 00000000..9fcac66e
--- /dev/null
+++ b/tests/Unit/views/layouts/app-custom-section.blade.php
@@ -0,0 +1,1 @@
+@yield('body')
diff --git a/tests/Unit/views/layouts/app-custom-slot.blade.php b/tests/Unit/views/layouts/app-custom-slot.blade.php
new file mode 100644
index 00000000..afad7af6
--- /dev/null
+++ b/tests/Unit/views/layouts/app-custom-slot.blade.php
@@ -0,0 +1,1 @@
+{{ $main }}
diff --git a/tests/Unit/views/layouts/app-extends.blade.php b/tests/Unit/views/layouts/app-extends.blade.php
new file mode 100644
index 00000000..2452a126
--- /dev/null
+++ b/tests/Unit/views/layouts/app-extends.blade.php
@@ -0,0 +1,3 @@
+@yield('content')
+
+{{ $bar }}