通过三个组件传递React props的最佳方式。

huangapple go评论73阅读模式
英文:

Best way to passing react props through three components

问题

export default function Header() {
  // ...
  return (
    <Stack>
      <Button onClick={handleDialog1}>my first button</Button>
      <Button onClick={handleDialog2}>my second button</Button>
      <Navbar opened1={opened1} opened2={opened2} handleDialog1={handleDialog1} handleDialog2={handleDialog2} />
    </Stack>
  );
}

export default function Navbar({ opened1, opened2, handleDialog1, handleDialog2 }) {
  return (
    <Stack>
      <Typography>lorem ipsum</Typography>
      <Typography>lorem ipsum</Typography>
      <Typography>lorem ipsum</Typography>
      <Dialog1 opened1={opened1} handleDialog1={handleDialog1} />
      <Dialog2 opened2={opened2} handleDialog2={handleDialog2} />
    </Stack>
  );
}

export default function Dialog1({ opened1, handleDialog1 }) {
  return (
    <Dialog open={opened1} onClose={handleDialog1}>
      <DialogTitle>
        <Typography>my first dialog</Typography>
        <IconButton onClick={handleDialog1} />
      </DialogTitle>
    </Dialog>
  );
}
英文:

I have this kind of components tree in my react app. My header component includes the navbar component which in turn includes dialog1 and dialog2.
So, I was wondering what is the best way to pass the props [opened1, opened2, handleDialog1, handleDialog2] to the dialog components from header (passing via navbar). Can you tell me the optimal solution?

     header
       |
     navbar
   |        |
dialog1  dialog2

header.js

import React, { useState } from &quot;react&quot;;
import { Button, Stack } from &quot;@mui/material&quot;;
import Navbar from &quot;components/Navbar&quot;;

export default function Header() {
  const [opened1, setOpened1] = useState(false);
  const [opened2, setOpened2] = useState(false);

  const handleDialog1 = () =&gt; {
    setOpened1(!opened1);
  };
  const handleDialog2 = () =&gt; {
    setOpened2(!opened2);
  };

  return (
    &lt;Stack&gt;
      &lt;Button onClick={handleDialog1}&gt;my first button&lt;/Button&gt;
      &lt;Button onClick={handleDialog2}&gt;my second button&lt;/Button&gt;
      &lt;Navbar /&gt;
    &lt;/Stack&gt;
  );
}

navbar.js

import React from &quot;react&quot;;
import { Typography, Stack } from &quot;@mui/material&quot;;
import Dialog1 from &quot;components/Dialog1&quot;;
import Dialog2 from &quot;components/Dialog2&quot;;

export default function Navbar() {
  return (
    &lt;Stack&gt;
      &lt;Typography&gt;lorem ipsum&lt;/Typography&gt;
      &lt;Typography&gt;lorem ipsum&lt;/Typography&gt;
      &lt;Typography&gt;lorem ipsum&lt;/Typography&gt;
      &lt;Dialog1 /&gt;
      &lt;Dialog2 /&gt;
    &lt;/Stack&gt;
  );
}

dialog1.js (dialog2.js is an equal component but with different numbers)

import React from &quot;react&quot;;
import { Typography, IconButton, Dialog, DialogTitle } from &quot;@mui/material&quot;;

export default function Dialog1() {
  return (
    &lt;Dialog open={opened1} onClose={handleDialog1}&gt;
      &lt;DialogTitle&gt;
        &lt;Typography&gt;my first dialog&lt;/Typography&gt;
        &lt;IconButton onClick={handleDialog1} /&gt;
      &lt;/DialogTitle&gt;
    &lt;/Dialog&gt;
  );
}

答案1

得分: 1

首先使对话框更通用

import React from "react";
import { Typography, IconButton, Dialog as MuiDialog, DialogTitle } from "@mui/material";

类型 DialogProps = {
    opened: boolean;
    handleDialog: () => void;
    title: string;
}

导出默认函数 对话框({opened, handleDialog, title}: DialogProps) {
    返回 (
        <MuiDialog open={opened} onClose={handleDialog}>
            <DialogTitle>
                <Typography>{title}</Typography>
                <IconButton onClick={handleDialog} />
            </DialogTitle>
        </MuiDialog>
    );
}

然后可以将子组件添加到导航栏

import React, {PropsWithChildren} from "react";

导出默认函数 导航栏(props: PropsWithChildren) {
    返回 (
        <div>
            <h1>lorem ipsum</h1>
            <h3>lorem ipsum</h3>
            <h3>lorem ipsum</h3>
            {props.children}
        </div>
    );
}

最后将对话框作为子组件传递 - 这样您就不需要传递任何属性。我想在您的情况下这是一个不错的决定,但在更复杂的情况下,可以考虑使用状态管理器。React 上下文通常用于全局应用程序内容,如应用程序语言或已验证用户

导出默认函数 头部() {
    const [opened1, setOpened1] = useState(false);
    const [opened2, setOpened2] = useState(false);

    const handleDialog1 = () => {
        setOpened1(!opened1);
    };
    const handleDialog2 = () => {
        setOpened2(!opened2);
    };

    返回 (
        <div>
            <button onClick={handleDialog1}>我的第一个按钮</button>
            <Button onClick={handleDialog2}>我的第二个按钮</Button>
            <Navbar>
                <Dialog opened={opened1} handleDialog={handleDialog1} title="我的第一个对话框" />
                <Dialog opened={opened2} handleDialog={handleDialog2} title="我的第二个对话框" />
            </Navbar>
        </div>
    );
}
英文:

First of all make Dialog more generic

import React from &quot;react&quot;;
import { Typography, IconButton, Dialog as MuiDialog, DialogTitle } from &quot;@mui/material&quot;;

type DialogProps = {
    opened: boolean;
    handleDialog: () =&gt; void;
    title: string;
}

export default function Dialog({opened, handleDialog, title}: DialogProps) {
    return (
        &lt;MuiDialog open={opened} onClose={handleDialog}&gt;
            &lt;DialogTitle&gt;
                &lt;Typography&gt;{title}&lt;/Typography&gt;
                &lt;IconButton onClick={handleDialog} /&gt;
            &lt;/DialogTitle&gt;
        &lt;/MuiDialog&gt;
    );
}

And then you can add children to Navbar

import React, {PropsWithChildren} from &quot;react&quot;;

export default function Navbar(props: PropsWithChildren) {
    return (
        &lt;div&gt;
            &lt;h1&gt;lorem ipsum&lt;/h1&gt;
            &lt;h3&gt;lorem ipsum&lt;/h3&gt;
            &lt;h3&gt;lorem ipsum&lt;/h3&gt;
            {props.children}
        &lt;/div&gt;
    );
}

Finally pass dialogs as children - this way you dont need to pass any props down. I suppose in your case its a good decision, but in more complex situations with lot nested components you can think of using state managers. React contexts is usually used for global app contenxt like app language or authentificated user

export default function Header() {
    const [opened1, setOpened1] = useState(false);
    const [opened2, setOpened2] = useState(false);

    const handleDialog1 = () =&gt; {
        setOpened1(!opened1);
    };
    const handleDialog2 = () =&gt; {
        setOpened2(!opened2);
    };

    return (
        &lt;div&gt;
            &lt;button onClick={handleDialog1}&gt;my first button&lt;/button&gt;
            &lt;Button onClick={handleDialog2}&gt;my second button&lt;/Button&gt;
            &lt;Navbar&gt;
                &lt;Dialog opened={opened1} handleDialog={handleDialog1} title=&quot;my first dialog&quot; /&gt;
                &lt;Dialog opened={opened2} handleDialog={handleDialog2} title=&quot;my second dialog&quot; /&gt;
            &lt;/Navbar&gt;
        &lt;/div&gt;
    );
}

答案2

得分: 0

我建议创建一个上下文并使用 useContext 钩子。这比传递 props 更可管理,而且比 redux 实现更简单。

以下是已经集成了按钮的上下文示例:

import { FC, createContext, useState } from "react";

export type FilterContextType = { 
    opened1: boolean;
    opened2: boolean;
};

export const FilterContext = createContext<FilterContextType | null>(null);

export const FilterProvider: FC<{children: any}> = 
    ({children }) => {

    const [opened1, setOpened1] = useState(false);
    const [opened2, setOpened2] = useState(false);

    return (
        <FilterContext.Provider value={{opened1: opened1, opened2: opened2}}>
            <div>
                <Button onClick={() => setOpened1(!opened1)}>我的第一个按钮</Button>
                <Button onClick={() => setOpened2(!opened2)}>我的第二个按钮</Button>
            </div>
            {children}
        </FilterContext.Provider>
    );
};

然后,你可以在 Header 组件中包装子组件:

return (
    <Stack>
        <FilterProvider>
            <Navbar />
        </FilterProvider>
    </Stack>
);

最后,在任何子组件中使用它:

const {opened1, opened2} = useContext(FilterContext) as FilterContextType;
英文:

I would suggest creating a context and using the useContext hook. It is a more manageable approach than passing props and requires less implementation than redux.

Here's the context with your buttons integrated:

import { FC, createContext, useState } from &quot;react&quot;;

export type FilterContextType = { 
    opened1: boolean;
    opened2: boolean;
};

export const FilterContext = createContext&lt;FilterContextType | null&gt;(null);

export const FilterProvider: FC&lt;{children: any}&gt; = 
    ({children }) =&gt; {

    const [opened1, setOpened1] = useState(false);
    const [opened2, setOpened2] = useState(false);

    return (
        &lt;FilterContext.Provider value={{opened1: opened1, opened2: opened2}}&gt;
            &lt;div&gt;
                &lt;Button onClick={() =&gt; setOpened1(!opened1)}&gt;my first button&lt;/Button&gt;
                &lt;Button onClick={() =&gt; setOpened2(!opened2)}&gt;my second button&lt;/Button&gt;
            &lt;/div&gt;
            {children}
        &lt;/FilterContext.Provider&gt;
    );
};

Then you would wrap you child components in Header component:

return (
    &lt;Stack&gt;
        &lt;FilterProvider&gt;
            &lt;Navbar /&gt;
        &lt;/FilterProvider&gt;
    &lt;/Stack&gt;
  );

And then use it in any child components:

const {opened1, opened2} = useContext(FilterContext) as FilterContextType;

答案3

得分: 0

以下是您要的内容的翻译:

你可以手动一路传递 props,但这可能会很繁琐。为了避免在大量嵌套中进行 prop 传递,你可以使用上下文(context)。

以下是一个示例,展示了如何实现这一点(为了简洁起见,省略了一些未更改的代码)。

header.js

import { useState, createContext } from 'react';
// ...
export const DialogContext = createContext(null); // 你也可以将这部分移到单独的文件中
// ...
export default function Header() {
	// ...
	
	return (
		<DialogContext.Provider value={{ opened1, handleDialog1, opened2, handleDialog2 }}>
			<Stack>
			  <Button onClick={handleDialog1}>我的第一个按钮</Button>
			  <Button onClick={handleDialog2}>我的第二个按钮</Button>
			  <Navbar />
			</Stack>
		</DialogContext.Provider>
	);
}

dialog1.js

// ...
import { useContext } from 'react';
import { DialogContext } from './header.js';
export default function Dialog1() {
	const { opened1, handleDialog1 } = useContext(DialogContext);
	return (
		<Dialog open={opened1} onClose={handleDialog1}>
		  <DialogTitle>
			<Typography>我的第一个对话框</Typography>
			<IconButton onClick={handleDialog1} />
		  </DialogTitle>
		</Dialog>
	);
}
英文:

You could pass the props all the way down manually, but that can be tedious. To avoid this prop drilling for a large amount of nesting, you can use a context.

Below is an example of how you could implement this (with some unchanged code omitted for brevity).

header.js:

import { useState, createContext } from &#39;react&#39;;
// ...
export const DialogContext = createContext(null); // you can also move this to a separate file
// ...
export default function Header() {
	// ...
	
	return (
		&lt;DialogContext.Provider value={{opened1, handleDialog1, opened2, handleDialog2}}&gt;
			&lt;Stack&gt;
			  &lt;Button onClick={handleDialog1}&gt;my first button&lt;/Button&gt;
			  &lt;Button onClick={handleDialog2}&gt;my second button&lt;/Button&gt;
			  &lt;Navbar /&gt;
			&lt;/Stack&gt;
		&lt;/DialogContext.Provider&gt;
	);
}

dialog1.js:

// ...
import { useContext } from &#39;react&#39;;
import { DialogContext } from &#39;./header.js&#39;;
export default function Dialog1() {
	const {opened1, handleDialog1} = useContext(DialogContext);
	return (
		&lt;Dialog open={opened1} onClose={handleDialog1}&gt;
		  &lt;DialogTitle&gt;
			&lt;Typography&gt;my first dialog&lt;/Typography&gt;
			&lt;IconButton onClick={handleDialog1} /&gt;
		  &lt;/DialogTitle&gt;
		&lt;/Dialog&gt;
	);
}

答案4

得分: -2

我建议您研究React状态管理,可以使用默认的Hooks或像Redux或Zustand这样的库。

英文:

I suggest u look into react state management either with the default hooks or with libraries like redux or zustand

huangapple
  • 本文由 发表于 2023年5月18日 06:57:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76276693.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定