2

I created two component App and Inner component App component contain a form .

My Inner component is containing a const Data further i passed that to dangerouslySetInnerHTML to set inner html . So there can be any number of input field . Sometime it can have 3 to 5 input field .

Is it possible in React? As i see i can't have two way binding at this scenario.

As i believe its not possible in React?

Demo Link

export default function App() {
  const handleSubmission = e => {
    e.preventDefault();
  };

  return (
    <div className="App">
      <Inner />
      <form onSubmit={handleSubmission}>
        <div>Both two values comma separated: </div>
        <button type="submit"> Submit </button>
      </form>
    </div>
  );
}
const data = {
  htmltag: `<html><style>table {  font-family: arial, sans-serif;  border-collapse: collapse;  width: 40%;}td, th {  border: 1px solid #dddddd;  text-align: left;  padding: 8px;}tr:nth-child(even) {  background-color: #dddddd;}</style><body> <table> <tbody> <tr> <th>1</th> <th>2</th> </tr> <tr> <td>Hello</td> <td><input type = "text" /></td> </tr>  <tr> <td>Welcome</td> <td><input type = "text" /></td> </tr> </tbody> </table> </body></html>`
};

export default function Inner() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <div dangerouslySetInnerHTML={{ __html: data.htmltag }} />
    </div>
  );
}

Khushboo
  • 67
  • 2
  • 9
  • Do you mean saving the input value? – tcf01 May 16 '20 at 17:18
  • How tied are you to using `dangerouslySetInnerHTML`? Would it be possible in your project to change the data to be in JSON format instead? If you're open to switching, I have some ideas on that as well. – Henry Woody May 16 '20 at 17:45

1 Answers1

1

You won't be able to get access to this data in a React-type way, however you can get access to this data using plain JavaScript, which React supports. Basically you can't treat these inputs as controlled components, instead as uncontrolled components.

This means however that you won't get state updates in real time like you do with controlled components, but you can still get access to the data. You'll just have to get the data from the DOM when you need it (e.g. upon submission).

Since you also won't be able to pass refs directly to these components, you'll have to query the DOM. You can start with a ref for your container (the one whose inner HTML gets dangerously set) and then query for input elements (and/or the other input-like elements).

Here's an example:

App.js

export default function App() {
  const dataContainerRef = React.useRef(null);

  const handleSubmission = e => {
    e.preventDefault();
    const inputs = Array.from(dataContainerRef.current.querySelectorAll("input"));
    // if your inputs have unique names:
    const formData = Object.fromEntries(inputs.map(input => [input.name, input.value]));
    // or just get an array of the values:
    const formDataArray = inputs.map(input => input.value);
  };

  return (
    <div className="App">
      <Inner dataContainerRef={ dataContainerRef } />
      <form onSubmit={handleSubmission}>
        <div>Both two values comma separated: </div>
        <button type="submit"> Submit </button>
      </form>
    </div>
  );
}

Inner.js

export default function Inner(props) {
  const { dataContainerRef } = props;

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <div
        ref={dataContainerRef}
        className="data-container"
        dangerouslySetInnerHTML={{ __html: data.htmltag }}
      />
    </div>
  );
}

Here's a codesandbox demo.

Note that you can look into this question about getting form data with JS if you want to know about other ways you can get the form data using plain JS (I'd recommend not importing jQuery into your React project).

Alternate Approach

If you're open to changing the structure of your data so that that data is not an HTML string and instead is in JSON format, you can dynamically create the input elements yourself and control them like normal.

For example, if you structure data like this:

const data = {
    inputNames: ["Hello", "Welcome"],
}

Then you can create formData of the form:

{
    Hello: "",
    Welcome: "",
}

which should be stored in the parent of Inner because it owns the submission function.

Then you'll just need to initialize the formData from data and then dynamically create an input element for each inputName given.

Here's a sample of Inner:

export default function Inner(props) {
  const { formData, setFormData } = props;

  React.useEffect(() => {
    // initialize `formData`
    setFormData(
      Object.fromEntries(data.inputNames.map(inputName => [inputName, ""]))
    );
  }, [setFormData]);

  const inputRows = data.inputNames.map(inputName => (
    <tr key={inputName}>
      <td>{inputName}</td>
      <td>
        <input
          name={inputName}
          value={formData[inputName]}
          onChange={event => {
            event.persist();
            setFormData(prev => ({
              ...prev,
              [event.target.name]: event.target.value
            }));
          }}
        />
      </td>
    </tr>
  ));

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>

      <table>
        <tbody>{inputRows}</tbody>
      </table>
    </div>
  );
}

Here's a demo.

Henry Woody
  • 14,024
  • 7
  • 39
  • 56
  • Can you give a codesandbox demo? because its not running @Henry Woody – Khushboo May 16 '20 at 17:54
  • @Khushboo I think so, you just need to pass the form data on to whatever function you need within `handleSubmit`. Also I updated my answer with an alternate approach. – Henry Woody May 16 '20 at 18:17