Reputation: 46813
Background
I have a simple ReactJS 3-step form which consumers use to request tech support from our partners.
Until now, the support request form was universal for all of our partners (name, email, problem)
Now, I'd like to for a specific partner, add additional form elements. Ideally this should be customizable on a per-partner basis. Example, Partner A, would like to also capture the serial number of the device. Partner B would like to have the customer agree to a terms of service.
To solve this, I am passing along an optional component to my form. If there, it is rendered. I have not figured out how to access the optional components data, and pass it along as an opaque data field as part of the request submission.
Question
In the code below, in Form
how can I access the state of FormOverride
?
Or, is there a more idomatic way to achieve what I am looking for?
Unlike a parent / child hierarchy where a parent can access the child state in a number of ways, the FormOverride
component is instantiated outside of the Form
hierarchy, and passed as a property.
import React from "react";
const Form = ({ override }) => {
//universal form state
const handleSubmit = () => {
if (override) {
//get the state value from override component
}
alert("Clicked");
};
return (
<>
<h3>Contact Info</h3>
{override}
<h3>Support Details</h3>
<h3>Case Summary</h3>
<button onClick={handleSubmit}>Submit</button>
</>
);
};
const FormOverride = () => {
const [text, setText] = React.useState("");
const handleChange = (e) => {
setText(e.target.value);
};
return (
<div>
<input type="text" value={text} onChange={handleChange} />
</div>
);
};
export default function App() {
const someValue = "composed";
let toRender = null;
if (someValue === "default") {
toRender = <Form/>;
}
if (someValue === "composed") {
toRender = <Form override=<FormOverride /> />;
}
return <div className="App">{toRender}</div>;
}
I also have a sandbox of the above.
Upvotes: 0
Views: 51
Reputation: 8915
In such cases, a global state manager like Redux is helpful but you can implement it without a global state manager with the state lift up
technique (as you mentioned in the comments) but in a tricky way:
Let's implement a state in the App
component and pass it to the related components:
export default function App() {
const [textOverride, setTextOverride] = React.useState("");
const someValue = "composed";
let toRender = null;
if (someValue === "default") {
toRender = <SomeComponent />;
}
if (someValue === "composed") {
toRender = (
<SomeComponent
override={<ComposedValue getOverrideText={setTextOverride} />}
textOverride={textOverride}
/>
);
}
return <div className="App">{toRender}</div>;
}
As you see, getOvverideText
props were passed to the ComposedValue
, so we need to call it when the input changes.
const ComposedValue = ({ getOverrideText }) => {
const [text, setText] = React.useState("");
const handleChange = (e) => {
const value = e.target.value;
setText(value);
getOverrideText(value);
};
return (
<div>
<input type="text" value={text} onChange={handleChange} />
</div>
);
};
Now, the textOvveride
value (which we defined in the App
) is up to date with text input changes. so we can easily use the textOverride
value in the SomeComponent
as below:
const SomeComponent = ({ override, textOverride }) => {
const handleSubmit = () => {
if (override) {
//get the state value from override component
}
alert(textOverride);
};
return (
<>
<h3>Contact Info</h3>
{override}
<h3>Support Details</h3>
<h3>Case Summary</h3>
<button onClick={handleSubmit}>Submit</button>
</>
);
};
Note: I prefer using a Global State Manager
like Redux to do such things within as the above implementation is hard to debug and change.
The bad side of this implementation is SomeComponent
will re-render with every single change in the input
element.
Note: there is no way to get the overrideText
as you call the handleSubmit
function because there aren't child-parent components. But, with using such tools as Redux, you can ignore the extra re-render and get the overrideText
at same the time as calling the handleSubmit
function.
Note: I guess you simplified the components to demonstrate your purpose, otherwise using an independent input
element inside of the SomeComponent
will reduce the codes and the procedure to get the changes from another component.
Upvotes: 1