key prop
Remember to check re-render when your activeFields.field changes, because you had set the key in your TextInput.
This will result in the TextInput component be unmount and create a new one
// 📌 check this state. Do not mutate to prevent re-render
this.state.activeFields?.fields?.map(field => {
const config = this.config.fields.find(fieldConfig =>
fieldConfig.key === field.key)
const inputConfig = {
type: config?.dataType.type,
id: config?.key,
label: config?.displayName,
required: false,
autofocus: false,
value: field.value
};
const inputBindings: ITextInputBindings = {}
return (
// 📌 if key be mutated from state, it will create a new component intead of old one
<div key={`${this.state.activeFields.key}-${field.key}`}>
<TextInput config={inputConfig} bindings={inputBindings}></TextInput>
</div>
)
})
Save Input value
And if you want to save the input value in TextInput, it is depends on which component you want to save the input value by state.
Save in the child component (In your case the TextInput)
Add a onChange event and a state in your TextInput component
Then add props because you are give props to it.
like this example edited from your code (maybe can not run, but the concept should work)
class TextInput extends Component<ITextInputConfig,ITextInputBindings> {
constructor(props) {
super(props);
this.state = { ...this.props }
}
// set state
const handleChange = (e) => {
this.setState({...this.state,
config: { ...this.state.config, value: e.target.value }
})
}
render() {
return (
<div className="textInput">
<Form.Group className="mb-3 textInput-group">
<Form.Label htmlFor={this.config.id}>{this.config.label}</Form.Label>
<Form.Control type={this.config.type}
placeholder={this.config.placeholder}
required={this.config.required}
id={this.config.id}
autoFocus={this.config.autofocus}
defaultValue={this.config.value}
// 📌 add onChange event on Form.Control
onChange={handleChange}
/>
</Form.Group>
</div>
);
}
}
Save in parent component
And if you need control or save state changes from parent component
add a state and a changeState function in your parent component, and give changeState to TextInput's props and let the
changeState prop mutate parent's value in child's input onChange event
example:
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = { inputValue: undefined }
}
const handleChange = (e) =>{
if(e.target)
this.setState({...this.state, inputValue: e.target.value});
}
render(){
return (
<div className="detailsPage-panel-right">
{
this.state.activeFields?.fields?.map(field => {
const config =
this.config.fields.find(fieldConfig =>
fieldConfig.key === field.key)
const inputConfig = {
type: config?.dataType.type,
id: config?.key,
label: config?.displayName,
required: false,
autofocus: false,
value: field.value
};
const inputBindings: ITextInputBindings = {}
return (
<div key=
{`${this.state.activeFields.key}-${field.key}`}
>
<TextInput
config={inputConfig}
bindings={inputBindings}
onChange={handleChange}>
</TextInput>
</div>
)
})
}
</div>
)
}
}
// TextInput
class TextInput extends Component<ITextInputConfig,ITextInputBindings> {
constructor(props) {
super(props);
this.state = { ...this.props }
}
const handleChange = (e) => {
this.props.onChange(e);
}
render() {
return (
<div className="textInput">
<Form.Group className="mb-3 textInput-group">
<Form.Label htmlFor={this.config.id}>{this.config.label} </Form.Label>
<Form.Control
type={this.config.type}
placeholder={this.config.placeholder}
required={this.config.required}
id={this.config.id}
autoFocus={this.config.autofocus}
defaultValue={this.config.value}
onChange={handleChange}/>
</Form.Group>
</div>
);
}
}
Code snippet example
a example that how child mutate parent's value, and how does the component destroyed when key changes. (written by functional component)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
function App () {
const [keys, setKeys] = React.useState([1, 2]);
const [inputValue, setInputValue] = React.useState(``);
const [inputValue2, setInputValue2] = React.useState(``);
const handleKeys = () =>{
let temp = [...keys];
temp[0] = temp[0] + 2;
temp[1] = temp[1] + 2;
setKeys([...temp])
}
return <div>
<div><button>Click this still remain the changes you had made</button></div>
<div><button onClick={handleKeys}>Click this to change keys, and will refresh the 'Number' prefix input component</button></div>
<br />
{
keys.map((key)=>{
if (key % 2 === 0) {
return <div key={key}>Number {key}: <Child setInputValue={setInputValue2}></Child></div>
}
else {
return <div key={key}>Number {key}: <Child setInputValue={setInputValue}></Child></div>
}
})
}
<br />
<div>child components that do not have key</div>
<div>First Child's Input: <Child setInputValue={setInputValue}></Child></div>
<div>Second Child's Input: <Child setInputValue={setInputValue2}></Child></div>
<br />
<div>inputValue(in parent from first child): {inputValue}</div>
<div>inputValue2(in parent from second child): {inputValue2}</div>
</div>
}
function Child ({ setInputValue }) {
const handleChange = (e) => {
if(setInputValue)
setInputValue(e.target.value);
}
return <input onChange={handleChange}></input>
}
</script>
<script type="text/babel">
ReactDOM.render(
<App></App>
, document.getElementById("root"));
</script>
Dynamically mutate and save input value by state
I guess you need save value dynamically by this.state.activeFields?.fields.
Create a state object for recording your active input value.
And add a handleChange function which can change value by e.target.id
// In your TextInput's parent
constructor(props) {
super(props);
this.state = { inputValues: {} }
}
const handleChange = (e)=>{
const changeField = this.state.activeFields?.fields.find(x=>x.key === e.target.key);
if(changeField) {
this.setState({...this.state.inputValues, changeField.key: e.target.value})
}
}
this.state.activeFields?.fields?.map( (field) => {
return (
<TextInput
config={inputConfig}
bindings={inputBindings}
// add onChange event
onChange={handleChange}
>
</TextInput>
)
})
more refernece:
Lifting State Up
Other
According to react-bootstrap's Form.Control API doc, should use the value intead of defaultValue