React - 将孩子分成几部分

Som*_*ial 5 reactjs react-native

想法

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

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

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;
Run Code Online (Sandbox Code Playgroud)

问题

然后我将以这种方式使用它(如下)。问题是 - 对于静态组件来说,一切都渲染得很好,但是一旦状态发生变化,您就会开始看到问题。

例如,当我输入<textarea />并onChanged被触发时,该函数会重新呈现,并且您会失去文本区域的焦点。所以最终你无法真正在文本区域中输入内容。

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>
  );

}
Run Code Online (Sandbox Code Playgroud)

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

问题

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

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

或者上面的风格到底可行吗?

PS:我已经使用了 context 和 redux,但我试图在这个例子中不使用它们。我试图保持简单。

And*_*rei 3

解决您的问题的一种方法是:使用 React Context API,而不是传递textand作为 props 。setText

具体来说:

  • 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>
  );
};
Run Code Online (Sandbox Code Playgroud)

请注意,FormBasic不再需要作为children函数或包装在 中的内容React.Fragment,使代码更清晰。您可以直接使用上下文消费者。