在服务器端内部
page.tsx
我有客户端组件SelectType.tsx
。并且应该像这样工作:
page.tsx
这是代码
page.tsx
import {SelectType} from "@/components/SelectType";
export default async function DownloadWithParams ({ params }: { params: { slug: string } }) {
let downloadParams: DlParameters = {}
const updateDlParams = async (newParams: DlParameters) => {
"use server"
downloadParams = newParams
console.log(downloadParams)
}
return(
<div className="bg-white w-[80%] min-h-[500px] rounded-2xl shadow-lg p-10 text-zinc-800">
<div className="parameters-container w-full px-[100px]">
<form className="flex">
<div className="choosetype-container mr-10 grow-[1] flex flex-col">
<label className="font-semibold block mb-2">Choose a type:</label>
<SelectType dlParams={downloadParams}
updateDlParams={updateDlParams}
/>
</div>
</form>
</div>
</div>
);
}
SelectType.tsx
'use client'
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select";
import {DlParameters} from "@/types";
type HandleFnc = (newParams: DlParameters) => void
export function SelectType({dlParams, updateDlParams} : {dlParams : DlParameters, updateDlParams : HandleFnc}) {
return (
<Select defaultValue={dlParams?.type || ""}
onValueChange={async (value: "video" | "audio") => {
dlParams.type = value || undefined;
await updateDlParams(dlParams);
}}>
<SelectTrigger className="grow border-2 border-gray-300 rounded-2xl font-semibold">
<SelectValue placeholder="Type" />
</SelectTrigger>
<SelectContent className="font-semibold" >
<SelectItem value="video">Video</SelectItem>
<SelectItem value="audio">Audio</SelectItem>
</SelectContent>
</Select>
);
}
错误信息
Error: downloadParams is not defined
Source
src\app\[slug]\page.tsx (25:9) @ downloadParams
23 | const updateDlParams = async (newParams: DlParameters) => {
24 | "use server"
> 25 | downloadParams = newParams
| ^
26 | console.log(downloadParams)
27 | }
28 |
我只是想了解为什么变量超出了函数的范围以及如何使其工作
首先,你不能以这种方式更改变量值,在 React 组件中声明普通变量并尝试更改它们的值是不可能的,但你可以使用 React State hook
useState
创建一个可以通过调用一些事件来更改,例如在您的情况下,您需要包含 "use client"
(选择使用客户端渲染)才能使用反应状态:
PS:由于您的 page.tsx
是客户端组件,因此您实际上不需要在 use client
组件中指定 SelectType.tsx
,因为它肯定是客户端组件。
// page.tsx
"use client";
import {SelectType} from "@/components/SelectType";
interface DownloadPageProps {
params: {
slug: string
}
}
export default async function DownloadWithParams ({ params }: DownloadPageProps) {
const [downloadParams,setDownloadParams] = useState<DlParameters>({})
// You should not use server action here, this is a very basic use case that does not require server actions at all.
const updateDlParams = (newParams: DlParameters) => {
// this will update the state
setDownloadParams(newParams)
}
return(
<div className="bg-white w-[80%] min-h-[500px] rounded-2xl shadow-lg p-10 text-zinc-800">
<div className="parameters-container w-full px-[100px]">
<form className="flex">
<div className="choosetype-container mr-10 grow-[1] flex flex-col">
<label className="font-semibold block mb-2">Choose a type:</label>
<SelectType dlParams={downloadParams}
updateDlParams={updateDlParams}
/>
</div>
</form>
</div>
</div>
);
}
// SelectType.tsx
/* ...importing modules */
interface SelectTypeProps {
dlParams : DlParameters,
updateDlParams : (newParams: DlParameters) => void
}
export function SelectType({dlParams, updateDlParams} : SelectTypeProps) {
return (
<Select defaultValue={dlParams?.type || ""}
onValueChange={(value: "video" | "audio") => {
updateDlParams({ ...dlParams, type: value });
}}>
<SelectTrigger className="grow border-2 border-gray-300 rounded-2xl font-semibold">
<SelectValue placeholder="Type" />
</SelectTrigger>
<SelectContent className="font-semibold" >
<SelectItem value="video">Video</SelectItem>
<SelectItem value="audio">Audio</SelectItem>
</SelectContent>
</Select>
);
}
PS:如果您不希望整个页面呈现在客户端,您可以为您的表单创建一个包装器(例如
DlParamsForm.tsx
)并在其中包含"use client"
以及状态逻辑,我强烈推荐这种方法.