如何在后代组件向祖先组件通知事件而无需使用 props。

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

How to notify an event without props from descendant to ancestor component

问题

我有一个组件(<Grid>),其中包含n个嵌套组件

  1. <Grid>
  2. <Header>
  3. <Toolbar>
  4. ...
  5. </Toolbar>
  6. <Filters>
  7. <Form>
  8. ...
  9. </Form>
  10. </Filters>
  11. </Header>
  12. <Body>
  13. <Datazone>
  14. ...
  15. </Datazone>
  16. <Summary>
  17. ...
  18. </Summary>
  19. </Body>
  20. <Footer>
  21. ...
  22. </Footer>
  23. </Grid>

我需要创建一个组件(<MagicButton>),可以将其放置在<Grid>树的任何位置。无论放置在树的哪个位置,按下它后应该执行某些操作,然后向<Grid>组件“通知”事件。
先决条件:我不想完全使用属性传递,所以我考虑了以下解决方案:
在上下文中包装<Grid>,并传递一个函数,该函数可以通过上下文值通知/更新<Grid>。这个函数是由使用上下文useContext<MagicButton>组件“触发”的。

这个方法是否正确?
如果是的话,是否可能通过上下文提供<MagicButton>组件,而不是“导入”,然后将<MagicButton>组件添加到网格的子组件<Toolbar>中,例如?
如果不是,是否有其他通知祖先组件的方法(显然不使用属性传递)?

  1. <GridPersistenceStateContext.Provider value={NotificationFunction}>
  2. <Grid>
  3. <Header>
  4. <Toolbar>
  5. <MagicButton />
  6. </Toolbar>
  7. <Filters>
  8. <Form>
  9. ...
  10. </Form>
  11. </Filters>
  12. </Header>
  13. <Body>
  14. <Datazone>
  15. ...
  16. </Datazone>
  17. <Summary>
  18. <MagicButton />
  19. </Summary>
  20. </Body>
  21. <Footer>
  22. <MagicButton />
  23. </Footer>
  24. </Grid>
  25. </GridPersistenceStateContext.Provider>

“自定义事件”方法是否代表可行的替代方法?
“自定义事件”是否会对正常的React生命周期造成问题?

英文:

I've a component (&lt;Grid&gt;) with n nested components

  1. &lt;Grid&gt;
  2. &lt;Header&gt;
  3. &lt;Toolbar&gt;
  4. ...
  5. &lt;/Toolbar&gt;
  6. &lt;Filters&gt;
  7. &lt;Form&gt;
  8. ...
  9. &lt;/Form&gt;
  10. &lt;/Filters&gt;
  11. &lt;/Header&gt;
  12. &lt;Body&gt;
  13. &lt;Datazone&gt;
  14. ...
  15. &lt;/Datazone&gt;
  16. &lt;Summary&gt;
  17. ...
  18. &lt;/Summary&gt;
  19. &lt;/Body&gt;
  20. &lt;Footer&gt;
  21. ...
  22. &lt;/Footer&gt;
  23. &lt;/Grid&gt;

I need to create a component (&lt;MagicButton&gt;) that can be placed everywhere into the &lt;Grid&gt; tree, wherever it is placed along the tree, when it's pressed it should do something and then "notify" an event to the &lt;Grid&gt; component.
prerequisite: I don't want to use props drilling at all, so I've thought of this solution:
To wrap the &lt;Grid&gt; in a context and pass a function that can notify/update the &lt;Grid&gt; in the context value. This function is "tirggered" from a &lt;MagicButton&gt; component that use the context useContext

is this approch correct?
If yes, is it possible to provide the &lt;MagicButton&gt; component through/by the context instead of "import" then &lt;MagicButton&gt; component into a grid's child component &lt;Toolbar&gt; e.g.
If not, is there another approch to notify to an anchestor component (without no props drilling obviously)

  1. &lt;GridPersistenceStateContext.Provider value={NotificationFunction}&gt;
  2. &lt;Grid&gt;
  3. &lt;Header&gt;
  4. &lt;Toolbar&gt;
  5. &lt;MagicButton /&gt;
  6. &lt;/Toolbar&gt;
  7. &lt;Filters&gt;
  8. &lt;Form&gt;
  9. ...
  10. &lt;/Form&gt;
  11. &lt;/Filters&gt;
  12. &lt;/Header&gt;
  13. &lt;Body&gt;
  14. &lt;Datazone&gt;
  15. ...
  16. &lt;/Datazone&gt;
  17. &lt;Summary&gt;
  18. &lt;MagicButton /&gt;
  19. &lt;/Summary&gt;
  20. &lt;/Body&gt;
  21. &lt;Footer&gt;
  22. &lt;MagicButton /&gt;
  23. &lt;/Footer&gt;
  24. &lt;/Grid&gt;
  25. &lt;GridPersistenceStateContext.Provider /&gt;

Does the "custom event" approch reppresent a viable alternative?
can "custom events" cause problems to the normal react lifecicle?

答案1

得分: 1

  1. 上下文提供了一种在组件之间共享这些值的方式,而不必显式地通过树的每一级传递prop。这是正确的方法。

  2. 可以通过上下文提供<MagicButton>组件,而不是在每个地方都导入<MagicButton>组件。我们可以使用SomeContext.Consumer,这是一种较旧的读取上下文的方式。这样我们就可以获取MagicButton组件并渲染它。

例如:

MagicButton.tsx

  1. import React from 'react';
  2. export const MagicButton = (props: any) => <button {...props}>magic button</button>;

context.ts

  1. import React from 'react';
  2. export const GridPersistenceStateContext = React.createContext<{
  3. notify: (from: string) => void;
  4. MagicButton: (props: any) => JSX.Element;
  5. }>(null!);
  1. import React from 'react';
  2. import { GridPersistenceStateContext } from './context';
  3. import { MagicButton } from './MagicButton';
  4. const Toolbar = ({ children }: React.PropsWithChildren) => <div>{children}</div>;
  5. export default function App() {
  6. const notificationFunction = (from: string) => {
  7. console.log('notified from: ', from);
  8. };
  9. return (
  10. <div className="App">
  11. <GridPersistenceStateContext.Provider value={{ notify: notificationFunction, MagicButton }}>
  12. <div className="Header">
  13. <Toolbar>
  14. Toolbar:
  15. <GridPersistenceStateContext.Consumer>
  16. {({ MagicButton, notify }) => (
  17. <MagicButton
  18. onClick={() => {
  19. notify('toolbar');
  20. }}
  21. />
  22. )}
  23. </GridPersistenceStateContext.Consumer>
  24. </Toolbar>
  25. </div>
  26. <div className="Body">
  27. <div className="Summary">
  28. Summary:
  29. <GridPersistenceStateContext.Consumer>
  30. {({ notify, MagicButton }) => (
  31. <MagicButton
  32. onClick={() => {
  33. notify('summary');
  34. }}
  35. />
  36. )}
  37. </GridPersistenceStateContext.Consumer>
  38. </div>
  39. </div>
  40. </GridPersistenceStateContext.Provider>
  41. </div>
  42. );
  43. }

codesandbox

英文:
  1. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree. It's the correct approach.

  2. It's possible to provide the &lt;MagicButton&gt; component through the context instead of import the &lt;MagicButton&gt; component everywhere. we can use SomeContext.Consumer, an older way to read context. So that we can get the MagicButton component and render it.

E.g.

MagicButton.tsx:

  1. import React from &#39;react&#39;;
  2. export const MagicButton = (props: any) =&gt; &lt;button {...props}&gt;magic button&lt;/button&gt;;

context.ts:

  1. import React from &#39;react&#39;;
  2. export const GridPersistenceStateContext = React.createContext&lt;{
  3. notify: (from: string) =&gt; void;
  4. MagicButton: (props: any) =&gt; JSX.Element;
  5. }&gt;(null!);
  1. import React from &#39;react&#39;;
  2. import { GridPersistenceStateContext } from &#39;./context&#39;;
  3. import { MagicButton } from &#39;./MagicButton&#39;;
  4. const Toolbar = ({ children }: React.PropsWithChildren) =&gt; &lt;div&gt;{children}&lt;/div&gt;;
  5. export default function App() {
  6. const notificationFunction = (from: string) =&gt; {
  7. console.log(&#39;notified from: &#39;, from);
  8. };
  9. return (
  10. &lt;div className=&quot;App&quot;&gt;
  11. &lt;GridPersistenceStateContext.Provider value={{ notify: notificationFunction, MagicButton }}&gt;
  12. &lt;div className=&quot;Header&quot;&gt;
  13. &lt;Toolbar&gt;
  14. Toolbar:
  15. &lt;GridPersistenceStateContext.Consumer&gt;
  16. {({ MagicButton, notify }) =&gt; (
  17. &lt;MagicButton
  18. onClick={() =&gt; {
  19. notify(&#39;toolbar&#39;);
  20. }}
  21. /&gt;
  22. )}
  23. &lt;/GridPersistenceStateContext.Consumer&gt;
  24. &lt;/Toolbar&gt;
  25. &lt;/div&gt;
  26. &lt;div className=&quot;Body&quot;&gt;
  27. &lt;div className=&quot;Summary&quot;&gt;
  28. Summary:
  29. &lt;GridPersistenceStateContext.Consumer&gt;
  30. {({ notify, MagicButton }) =&gt; (
  31. &lt;MagicButton
  32. onClick={() =&gt; {
  33. notify(&#39;summary&#39;);
  34. }}
  35. /&gt;
  36. )}
  37. &lt;/GridPersistenceStateContext.Consumer&gt;
  38. &lt;/div&gt;
  39. &lt;/div&gt;
  40. &lt;/GridPersistenceStateContext.Provider&gt;
  41. &lt;/div&gt;
  42. );
  43. }

codesandbox

huangapple
  • 本文由 发表于 2023年7月20日 14:01:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/76727065.html
匿名

发表评论

匿名网友

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

确定