首页 > 解决方案 > React - 将孩子分成几部分

问题描述

理念

我正在尝试创建一个被拆分为多个部分的组件。

例如,在下面的示例代码中,我的目标是在保持状态的同时将表单和操作分开。这样,我可以创建一个基本的表单+操作按钮组件,然后在不同的页面上设置不同的样式。

import  { useState } from 'react'

const FormBasic = ({ children }) => {
  const [ text, setText ] = useState('')

  const onSubmit = () => console.log("Submitting");
  const onChange = (e) => {
    console.log("Typing", e.target.value);
    setText(e.target.value)
  }

  const Form = () => <textarea rows={4} onChange={onChange} value={text} />;

  const Actions = () => <button onClick={onSubmit}>Some Buttons</button>;

  return children({ Form, Actions });
};

export default FormBasic;

问题

然后我将以这种方式使用它(如下)。问题是 - 对于静态组件来说,这一切都很好,但你会在状态改变的那一刻开始看到问题。

例如,当我输入<textarea />andonChanged被触发时,函数会重新呈现,并且您会失去对文本区域的关注。所以最终你不能真正输入文本区域。

export default function App() {
  return (
      <FormBasic> 
      {({ Form, Actions }) => {
          return (<React.Fragment>
                  <div style={{ border: '1px solid red'}}>
                    <Form />
                  </div>
                  <div style={{ border: '1px solid blue'}}>
                     <Actions />
                  </div>
            </React.Fragment>)

      }}

      </FormBasic>
  );

}

我包括一个代码沙箱 - https://codesandbox.io/s/solitary-brook-nrrbf?file=/src/App.js

问题

这类组件的实际使用可能存在疑问。实际上,我如何修改我的代码,以便我可以继续使用导出的textarea.

或者是否有其他形式的导出组件以便我可以拆分它们并维护状态?

或者上述风格是否可能?

PS:我用过context和redux,在这个例子中我尽量不使用它们。我试图保持简单。

标签: reactjsreact-native

解决方案


您的问题的一种解决方案是:使用 React Context API代替传递textsetText作为道具。

具体来说:

  • FormBasic将成为状态+上下文提供者。
  • Form并将Actions成为上下文消费者。
  • 上下文将包含状态 + 状态设置器。

这是我想出的(CodeSandbox 上的工作示例):

(我为状态变量使用了更通用的名称来帮助这篇文章的其他读者)。

import React from "react";
import { useState, useContext, createContext } from "react";

// create the context with some generic defaults
const StateContext = createContext({
  state: "",
  setState: v => v
});

// create the context hook
const useStateContext = () => useContext(StateContext);

// the textarea component
const Form = () => {
  const { state, setState } = useStateContext();

  const onChange = (e) => {
    console.log("Typing", e.target.value);
    setState(e.target.value);
  };

  return <textarea rows={4} onChange={onChange} value={state} />;
};

// the buttons component
const Actions = () => {
  const { state } = useStateContext();

  const onSubmit = () => {
    alert(state);
  };

  return <button onClick={onSubmit}>Some Buttons</button>;
};

// the form itself holding state and the context provider
const FormBasic = ({ children }) => {
  const [state, setState] = useState("");

  return (
    <StateContext.Provider value={{ state, setState }}>
      {children}
    </StateContext.Provider>
  );
};

const App = () => {
  return (
    <FormBasic>
      <div style={{ border: "1px solid red" }}>
        <Form />
      </div>
      <div style={{ border: "1px solid blue" }}>
        <Actions />
      </div>
    </FormBasic>
  );
};

请注意,FormBasic不再需要有childrenas 函数,或者包含在 中的内容React.Fragment,使代码更干净。您可以直接使用上下文消费者。


推荐阅读