Dynamic form content
First off this library is really great.
I ran into an issue where Modals with custom content dependent on container state aren't re-rendered due to the ModalWidget cloning the elements once at mount time. I was hoping to have a more dynamic modal which would show / hide a TextAreaWidget dependent on the value of a SwitchWidget ala below:
<GiftedForm.ModalWidget
ref="customMessageModal"
title='Custom Message'
displayValue='hasCustomMessage'
underlayColor='royalblue'
transformValue={(value) => (value ? 'on' : 'off') }
>
<GiftedForm.SeparatorWidget />
<GiftedForm.SwitchWidget
name='hasCustomMessage'
title='Custom Message? (+ $1.00)'
value={this.state.hasCustomMessage}
onValueChange={(value) => {
this.setState({ hasCustomMessage: value })
}}
/>
{this.state.hasCustomMessage ? <GiftedForm.TextAreaWidget
name='customMessage'
value={this.state.customMessage}
placeholder='Custom Message'
maxLength={120}
multiline={true}
style={styles.customMessage}
onChangeText={(value) => {
this.setState({ customMessage: value })
}}
/> : <View />}
<GiftedForm.NoticeWidget title='Optionally add a custom welcome message that will be sent before any other messages. (Max 120 characters)' />
</GiftedForm.ModalWidget>
Any idea how to make the modal contents more dynamic?
Thanks!
Hello @fisch0920, I can't test your code right now but I think your onValueChange is overwritten by https://github.com/FaridSafi/react-native-gifted-form/blob/master/widgets/SwitchWidget.js#L61 You must replace line 61 by something like :
onValueChange={(value) => {
this._onChange(value);
this.props.onChange(value);
}}
then to do
onChange={(value) => {
this.setState({ hasCustomMessage: value })
}}
then to do something similar for TextAreaWidget
If it works, please submit a pull request
Hey @FaridSafi, thanks for the quick feedback and I know what you mean. I've already changed my fork to allow onValueChange overrides in addition to calling WidgetMixin._onChange, but the base issue remains that updating the parent state doesn't update the cloned modal's child states.
If I change ContainerMixin as follows, dynamic content within the form itself and modals works as expected.
From:
componentWillMount() {
this._childrenWithProps = React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, {
formStyles: this.props.formStyles,
openModal: this.props.openModal,
formName: this.props.formName,
navigator: this.props.navigator,
onFocus: this.handleFocus,
onBlur: this.handleBlur,
});
});
},
_renderContainerView() {
if (this.props.scrollEnabled === true) {
return (
<ScrollView
ref='container'
style={[(this.props.isModal === false ? styles.containerView : styles.modalView), this.props.style]}
automaticallyAdjustContentInsets={false}
keyboardDismissMode='on-drag'
keyboardShouldPersistTaps={true}
onTouchStart={this.props.scrollOnTap === true ? this._onTouchStart : null}
onScroll={this.props.scrollOnTap === true ? this._onScroll : null}
scrollEventThrottle={this.props.scrollOnTap === true ? 200 : 0}
{...this.props}
>
{this._childrenWithProps}
</ScrollView>
);
}
return (
<View
ref='container'
style={[(this.props.isModal === false ? styles.containerView : styles.modalView), this.props.style]}
keyboardDismissMode='on-drag' // its working on View ?
{...this.props}
>
{this._childrenWithProps}
</View>
);
},
To:
componentWillMount() {
this._childrenWithProps = () => React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, {
formStyles: this.props.formStyles,
openModal: this.props.openModal,
formName: this.props.formName,
navigator: this.props.navigator,
onFocus: this.handleFocus,
onBlur: this.handleBlur,
});
});
},
_renderContainerView() {
if (this.props.scrollEnabled === true) {
return (
<ScrollView
ref='container'
style={[(this.props.isModal === false ? styles.containerView : styles.modalView), this.props.style]}
automaticallyAdjustContentInsets={false}
keyboardDismissMode='on-drag'
keyboardShouldPersistTaps={true}
onTouchStart={this.props.scrollOnTap === true ? this._onTouchStart : null}
onScroll={this.props.scrollOnTap === true ? this._onScroll : null}
scrollEventThrottle={this.props.scrollOnTap === true ? 200 : 0}
{...this.props}
>
{this._childrenWithProps()}
</ScrollView>
);
}
return (
<View
ref='container'
style={[(this.props.isModal === false ? styles.containerView : styles.modalView), this.props.style]}
keyboardDismissMode='on-drag' // its working on View ?
{...this.props}
>
{this._childrenWithProps()}
</View>
);
}
Note that this is just making the childrenWithProps cloning dynamic instead of only at mount time. This change is necessary for my use case to function properly as I want to rely on external state to update the form's contents, not just the form's internal state representation. @FaridSafi what do you think of this change? Will it have any unexpected side effects?
Looks good, can you enable it as an option (something like dynamicForm={true}) and submit a PR please ?
We can include this in next version.
Also, I think you should use widgetStyles prop instead of style for the TextAreaWidget
widgetStyles={{
textArea: styles.customMessage
}}