理解Svelte中的上下文(从React上下文转换)



我有一个使用ContextAPI管理身份验证的react应用程序,我正在尝试在Svelte中实现类似的功能。

Authenticate.js中,我有这个:

import React, { useContext, useState, useEffect } from "react"
import { auth } from "../firebase"
const AuthCt = React.createContext()
export function Auth() {
return useContext(AuthCt)
}
export function AuthComp({ children }) {
const [currentUser, setCurrentUser] = useState()
const [loading, setLoading] = useState(true)
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password)
}
function logout() {
return auth.signOut()
}
useEffect(() => {
const unmount = auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unmount
}, [])
const value = {
currentUser,
login,
signup
}
return (
<AuthCt.Provider value={value}>
{!loading && children}
</AuthCt.Provider>
)
}

此上下文用于其他Login.js组件,如下所示:

import { Auth } from "./Authenticate"
const Login = () => {
const { currentUser, login } = Auth()

App.js中,我有:

import { AuthComp } from "./Authenticate";
function App() {
return (
<AuthComp>
<div> All others go here </div>
</AuthComp>
);
}

如何在Svelte中实现这一点,尤其是在Authenticate上下文中?

我在斯维尔特没能做什么,因为我不知道如何从这里开始。到目前为止,我有AuthComp.svelte。我不知道我做的事情是否正确。

<script>
import { getContext, setContext } from 'svelte';
import  { auth } from '../firebase';
import { writable } from 'svelte/store';
let Auth = getContext('AuthCt')
setContext('Auth', Auth)
let currentUser;
let loading = true;

const unmount = auth.onAuthStateChanged(user => {
currentUser = user;
loading = false
});

function login(email, password) {
return auth.signInWithEmailandPassWord(email,password)
}

function logout() {
return auth.signOut()
}

const value = { currentUser, login, signUp }

</script>
<slot value={value}></slot>

从React Context迁移到Svelte

Svelte和React中的上下文看起来可能相似,但实际上它们的用法不同。因为从本质上讲,Svelte的背景要有限得多。但没关系。事实上,它实际上会让你的代码更容易编写和理解。

在Svelte中,您可以使用更多的工具在应用程序中传递数据(并保持同步),而不仅仅是上下文。每个人都做了一件事(让一切都可预测),而且他们做得很好。其中,您有:

  • 上下文
  • 商店
  • 道具

作为一个最近从React转到Svelte的人,我想我可以帮助解释每一个之间的一些差异,并帮助您避免我的一些概念错误。我还将讨论生命周期方法的一些差异,因为如果你过去使用useEffect,你可能会感到非常失落,因为Svelte没有等效的API。然而,在Svelte将一切结合在一起会让一切变得简单。

上下文

Svelte中的上下文做一件事:将数据从父组件传递给任何子组件(不一定是直接子组件)。与React不同,上下文不是反应性的。它在组件装载时设置一次,然后不会再次更新。我们将进入";反应上下文";在一秒钟内。

<!-- parent.svelte -->
<script>
import { setContext } from 'svelte'
setContext('myContext', true)
</script>
<!-- child.svelte -->
<script>
import { getContext } from 'svelte'
const myContext = getContext('myContext')
</script>

请注意,上下文涉及两件事,一个键和一个值。将上下文设置为特定键,然后可以使用该键检索值。与React不同,您不需要导出函数来检索上下文。上下文的键和值都可以是任何值。如果可以将其保存到变量中,则可以将其设置为上下文。你甚至可以用一个物体作为钥匙!

商店

如果你的应用程序中有需要在多个地方保持同步的数据,那么商店就是你的选择。商店是被动的,这意味着它们可以在创建后进行更新。与React或Svelte中的上下文不同,商店不会简单地将数据传递给他们的孩子。应用程序的任何部分都可以创建商店,应用程序的任意部分都可以读取商店。您甚至可以在单独的JavaScript文件中创建Svelte组件之外的存储。

// mystore.ts
import { writable } from 'svelte/store'
// 0 is the initial value
const writableStore = writable(0)
// set the new value to 1
writableStore.set(1)
// use `update` to set a new value based on the previous value
writableStore.update((oldValue) => oldValue + 1)
export { writableStore }

然后在组件内部,您可以订阅存储。

<script>
import { writableStore } from './mystore'
</script>
{$writableStore}

美元标志向商店订购。现在,无论何时更新存储,组件都会自动重新发送。

将存储与上下文一起使用

既然我们有了存储和上下文,我们就可以创建"反应上下文";(我刚编的一个术语,但它有效)。存储非常好,因为它们是被动的,上下文非常适合将数据传递给子组件。但我们实际上可以通过上下文传递一个商店。这使得上下文是被动的,并且存储的范围是有限的。

<!-- parent.svelte -->
<script>
import { setContext } from 'svelte'
import { writable } from 'svelte/store'
const writableStore = writable(0)
setContext('myContext', writableStore)
</script>
<!-- child.svelte -->
<script>
import { getContext } from 'svelte'
const myContext = getContext('myContext')
</script>
{$myContext}

现在,每当父级中的存储更新时,子级也会更新。商店当然可以做得更多,但如果你想复制React上下文,这是你在Svelte能买到的最接近的。它也少了很多样板!

使用";反应上下文";用";useEffect">

Svelte没有useEffect的等价物。相反,Svelte的说法是被动的。在文档/教程中有很多关于这些的内容,所以我会简要介绍一下。

// doubled will always be twice of single. If single updates, doubled will run again.
$: doubled = single * 2
// equivalent to this
let single = 0
const [doubled, setDoubled] = useState(single * 2)
useEffect(() => {
setDoubled(single * 2)
}, [single])

Svelte足够聪明,能够计算出依赖关系,并且只根据需要运行每个反应语句。如果你创建了一个依赖循环,编译器就会对你大喊大叫。

这意味着您可以使用反应式语句来更新存储(从而更新上下文)。这里,valueStore将在对输入的每次击键时更新。由于这个存储是通过上下文传递的,因此任何子级都可以获得输入的当前值。

<script>
import { setContext } from 'svelte'
import { writable } from 'svelte/store'
// this value is bound to the input's value. When the user types, this variable will always update
let value
const valueStore = writable(value)
setContext('inputContext', valueStore)
$: valueStore.set(value)
</script>
<input type='text' bind:value />

道具

在大多数情况下,道具在React和Svelte中的作用完全相同。有一些区别,因为Svelte道具可以利用双向绑定(不必要,但可能)。不过,这真的是一个不同的对话,教程非常擅长教授道具双向绑定。

Svelte中的身份验证

好的,在所有这些之后,让我们看看如何创建身份验证包装器组件。

  • 创建身份验证存储
  • 通过上下文传递身份验证存储
  • 使用Firebase的onAuthStateChanged侦听身份验证状态的更改
  • 订阅子级中的身份验证存储
  • 父级被销毁时取消订阅onAuthStateChanged以防止内存泄漏
<!-- parent.svelte -->
<script>
import { writable } from 'svelte/store'
import { onDestroy, setContext } from 'svelte'
import { auth } from '../firebase'
const userStore = writable(null)
const firebaseUnsubscribe = auth.onAuthStateChanged((user) => {
userStore.set(user)
})
const login = (email, password) => auth.signInWithEmailandPassWord(email,password)
const logout = () => auth.signOut()
setContext('authContext', { user: userStore, login, logout })
onDestroy(() => firebaseUnsubscribe())
</script>
<slot />
<!-- child.svelte -->
<script>
import { getContext } from 'svelte'
const { login, logout, user } = getContext('authContext')
</script>
{$user?.displayName}

在Svelte中,上下文是用父组件中的setContext(key, value)设置的,子级可以用getContext(key)访问value对象。有关详细信息,请参阅文档。

在您的情况下,上下文的使用方式如下:

<script>
import { getContext, setContext } from 'svelte';
import  { auth } from '../firebase';
import { writable } from 'svelte/store';
// you can initialize this to something else if you want
let currentUser = writable(null)
let loading = true

// maybe you're looking for `onMount` or `onDestroy`?
const unmount = auth.onAuthStateChanged(user => {
currentUser.set(user)
loading = false
});

function login(email, password) {
return auth.signInWithEmailandPassWord(email,password)
}

function logout() {
return auth.signOut()
}

const value = { currentUser, login, signUp }
setContext('Auth', value) 

</script>
{#if !loading}
<slot></slot>
{/if}

这里,currentUserloginsignup(不确定它来自哪里?)被设置为setContext()的上下文。要使用这种上下文,您可能会有这样的东西:

<!-- App -->
<AuthComp>
<!-- Some content here -->
<Component />
</AuthComp>
<!-- Component.svelte -->
<script>
import { getContext } from 'svelte'
const { currentUser, login, signup } = getContext('Auth')
// you can subscribe to currentUser with $currentUser
</script>
<div>some content</div>

正如在文档中所写的,上下文是而非反应性,因此currentUser首先被转换为存储,以便可以在子级中订阅。至于useEffect,Svelte具有生命周期函数,您可以使用这些函数在不同的点运行代码,例如onMountonDestroy

如果你是Svelte的新手,他们的教程非常全面,有很多例子可以参考。

希望这能有所帮助!

最新更新