在 React.js 中正确使用 Modals(零 prop 钻取)
如果控制模态框就像编写以下效果一样简单会怎样?
const someModal = useModal()
useEffect(() => {
  if (someModal.isOpen) {
    setTimeout(someModal.close, 1000)
  }
}, [someModal])
我的名字是 Itay Schechner,我是一名成长中的全栈开发人员,专门研究前端后端代码,尤其是 React.js。
在本文中,我将教您如何编写可读、可重用的模态实用程序。
注意:本文主要基于我之前写的一篇文章,详细解释了 Context API 的用法。
今天你将学到:
- useModal 钩子的用法
- 模态组件工厂
- 使用模态工厂编写可读代码。
模态钩子
让我们从一些 TypeScript 开始:
export interface Modal {
  isOpen: boolean;
  open(): void;
  close(): void;
}
由此,我们了解到每个模态框都能够自行打开、关闭,并“告知”使用它的组件和钩子它是否处于打开状态。这个钩子相对容易实现:
export default function useModal(): Modal {
  const [isOpen, setOpen] = useState(false);
  return {
    isOpen,
    open() {
      setOpen(true);
    },
    close() {
      setOpen(false);
    },
  };
}
你可以在某个组件中使用此钩子,并大量使用 prop 语句来实现模态逻辑。例如:
export default function Navbar ()  {
    const { isOpen, open, close } = useModal();
    return (
        <nav>
         // ...navigation code
         { isOpen && <Modal close={close} /> }
         <button onClick={open}>Open Modal</button>
        </nav>
    )
}
因为我们太习惯这样编写组件了,所以我们没有意识到模态框的全部潜力。如果你的模态框文件导出如下会怎么样?
import LoginModal, { LoginModalOpener } from '../auth/LoginModal';
模态工厂
与我们之前讨论过的组件工厂不同,这个工厂将更加复杂。
让我们再次从一些 TypeScript 开始,看看这个工厂的要求。
export function createModal<T extends object>(
  context: Context<T>,
  name: keyof T,
  openerLabel: string
) { ... }
我们从中了解到了什么?
- 该函数将在提供的上下文中获取一个 Modal 类型的字段,并使用它来创建模态框
- 该函数采用 openerLabel 字段,这意味着它也将创建开启按钮。
- 如果我们提供了开场白,那么我们也应该能够提供结尾白。我希望结尾白显示一个 x 图标而不是文本,所以我会先升级上下文动作工厂。
type JSXProvider<Props> = (props: Props) => JSX.Element;
export function action<T extends object, Props extends object = {}>(
  label: string | JSXProvider<Props>, 
  context: React.Context<T>,
  consumer: (ctx: T) => void,
) {
  return function ContextAction({ className, ...props }: withClass & Props) {
    const ctx = useContext(context);
    const action = useCallback(() => consumer(ctx), [ctx]);
    return (
      <button onClick={action} className={className}>
        {typeof label === 'string' ? label : label(props as unknown as Props)}
      </button>
    );
  };
}
现在,我们可以编写我们的模式工厂:
export function createModal<T extends object>(
  context: Context<T>,
  name: keyof T,
  openerLabel: string
) {
  return {
    Visible: createWrapper(
      context,
      ctx => (ctx[name] as unknown as ModalHook).isOpen
    ),
    Opener: action(openerLabel, context, ctx =>
      (ctx[name] as unknown as Modal).open()
    ),
    // Clear: A JSXProvider that takes width and height props
    Closer: action(Clear, context, ctx => 
      (ctx[name] as unknown as Modal).close()
    ),
  };
}
让我们看看如何使用这个工厂创建简洁的代码。在我将要展示的示例中,我将在身份验证上下文中创建一个登录模式,该身份验证上下文在 App.tsx 文件中为整个应用程序提供。
// AuthContext.tsx
export default function AuthContextProvider({ children }: Wrapper) {
  // other auth state ommited for bravety
  const loginModal = useModal();
  // effects ommitted for bravety
  return (
    <AuthContextProvider value={{ loginModal, ...anything }}>{ children }</AuthContextProvider>
  )
} 
// LoginModal.tsx
const ModalProvider = createModal(AuthContext, 'loginModal', 'Log In');
export const LoginModalOpener = ModalProvider.Opener;
export default function LoginModal() {
    return (
        <ModalProvider.Visible> // modal is hidden when hook state is hidden
            // Modal UI (i.e dark fixed background, white modal)
            <ModalProvider.Closer />
            <div>
                // form ommited for bravety
            </div>
        </ModalProvider.Visible>
    )
}
// App.tsx
export default function App () {
    return (
        <AuthContextProvider>
            <LoginModal />
            <Navbar />
            // rest of application
        </AuthContextProvider>
    )
}
现在,让我们看看我们的导航栏组件变得多么简单:
import { LoginModalOpener } from '../auth/LoginModal';
export default function Navbar () {
    return (
        // ... links ommited for bravety
        <LoginModalOpener />
    )
}
总结
如果您认为我犯了错误或者我可以写得更好,请提出建议。
我使用过这个的项目 -
文章来源:https://dev.to/itays123/using-modals-in-react-js-the-right-way-zero-prop-drilling-3ah9 后端开发教程 - Java、Spring Boot 实战 - msg200.com
            后端开发教程 - Java、Spring Boot 实战 - msg200.com