【问题标题】:How to lift up sub-component data from within custom component - e.target.value always undefined如何从自定义组件中提升子组件数据 - e.target.value 始终未定义
【发布时间】:2019-09-23 02:06:40
【问题描述】:

我有一个注册页面,可以根据this.state 中的布尔值在两种形式之间切换。如果 bool == true 则显示个人注册表单,如果 == false 则显示业务注册表单。一次只能看到一个表格(我已经在这里工作了)。当我试图保留注册表单的状态时,我会感到困惑,而不管 bool 值如何。即,如果您开始填写个人表格并切换到业务表格,我不想丢失这些条目。我看到解决方案是将表单进度存储在注册页面的状态中。

我正在尝试提升每个 *SignUpForm 组件的状态,并在 this.statepersonalFormbusinessForm 的两个子对象中跟踪它们。但是,每当我输入文本时,例如,名字字段 TextInput 我都会收到一条错误消息:

TypeError: undefined is not an object (evalating 'form.first')

所以我很自然地开始在提升工作流程中放置console.log 语句,这是我尝试开始将数据输入表单后打印的内容:

Handling internal personal form change!
Lifting up:
undefined
Handling personal change in SignUp page
Existing personal form:
Object {
  "email": "",
  "first": "",
  "last": "",
}
Updating personal form to:
undefined
Completed update, new personal form:
Object {
   "email": "",
  "first": "",
  "last": "",
}
undefined
Rendering personal form!
undefined
Rendering personal form!
undefined

如您所见,我要提升的数据是undefined。我开始弄乱PersonalSignUpForm 中哪个组件具有onChange 方法。现在它是根 View 组件的一个道具,当然,它不起作用。我尝试将它放在实际的 TextInput 本身中,但仍然产生了相同的未定义错误。

所以,总而言之,我假设我的状态提升失败了,因为我的 *SignUpForm 组件不仅仅是一个组件/复合体,而且 React 无法推断出 e.target.value 是什么。如何更改我的提升代码以正确捕获对TextInput 的所有更改并将它们存储在 SignUp.js 的状态中?

SignUp.js

export default class SignUpScreen extends React.Component {
    constructor(props){
        super(props);
        this._handlePersonalAccountPress = this._handlePersonalAccountPress.bind(this);
        this._handleBusinessAccountPress = this._handleBusinessAccountPress.bind(this);
        this._handlePersonalFormChange = this._handlePersonalFormChange.bind(this);
        this._handleBusinessFormChange = this._handleBusinessFormChange.bind(this);

        this.state = {
            personalAccount: true,

            personalForm: {
                first: '',
                last: '',
                email: ''
            },

            businessForm: {
                company: '',
                email: ''
            }
        };
    }

    _handlePersonalAccountPress() {
        this.setState({personalAccount: true})
    }

    _handleBusinessAccountPress() {
        this.setState({personalAccount: false})
    }

    _handlePersonalFormChange(newForm) {
        console.log('Handling personal change in SignUp page')
        console.log('Existing personal form:')
        console.log(this.state.personalForm)
        console.log('Updating personal form to:')
        console.log(newForm)
        this.setState({personalForm: newForm})
        console.log('Completed update, new personal form:')
        console.log(this.state.personalForm)
    }

   render(){

        if (this.state.personalAccount) 
        {
            console.log(this.state.personalForm)
            form = <PersonalSignUpForm
                        _onFormChange={this._handlePersonalFormChange}
                        form={this.state.personalForm}/>;
        } 
        else 
        {
            form = <BusinessSignUpForm
                        _onFormChange={this._handleBusinessFormChange}
                        form={this.state.businessForm}/>;          
        }
        return (
            <KeyboardAvoidingView behavior="padding">
                <StatusBar barStyle="dark-content" />
                <View>
                    <AccountTypeButton
                        text='Personal'
                        onAccountChange={this._handlePersonalAccountPress} 
                        selected={this.state.personalAccount}/>
                    <AccountTypeButton 
                        text='Business'
                        onAccountChange={this._handleBusinessAccountPress} 
                        selected={!this.state.personalAccount}/>
                </View>
                {form}
                <View>
                    <TouchableOpacity
                        onPress={() => this._onCreateAccountPress()}>
                        <Text>Create Account</Text>
                    </TouchableOpacity>
                </View>
            </KeyboardAvoidingView>
        );
    }
  }

PersonalSignUpForm.js

BusinessSignUpForm.js 相同,只是 TextInput 不同

export default class PersonalSignUpForm extends Component {
    constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange (e) {
        console.log('Handling internal personal form change!')
        console.log('Lifting up:')
        console.log(e.target.value)
        this.props._onFormChange(e.target.value);
    }

    render() {
        console.log('Rendering personal form!')
        console.log(this.props.form)
        const form = this.props.form

        return (
            <View style={styles.forms}
                onChange={this.handleChange}>
                <View style={styles.textInputBorder}>
                    <TextInput style={styles.textInput}
                        value={form.first} 
                        editable
                        placeholder='first name'/>
                    </View>
                    <View style={styles.textInputBorder}>
                    <TextInput style={styles.textInput}
                        value={form.last} 
                        editable
                        placeholder='last name'/>
                    </View>
                    <View style={styles.textInputBorder}>
                    <TextInput style={styles.textInput}
                        value={form.email} 
                        editable
                        placeholder='email'
                        keyboardType='email-address'/>
                    </View>
                </View>
        );
    }
}

【问题讨论】:

  • 如果你有类似CodeSandbox 的东西来演示发生了什么,那就太好了。

标签: javascript reactjs react-native


【解决方案1】:

是的,提升状态是最佳实践,老实说,这是实现跨组件层次结构的干净数据流的唯一方法。但是,我的意思是您试图通过引用从 props 传递的值直接在子组件中修改父组件的状态。

您应该尝试通过将函数作为 prop 传递给子组件并调用该 fn 来通过子组件中更改的值来更新父组件的状态,如下例所示。

https://codesandbox.io/embed/clever-mirzakhani-8v4ly?fontsize=14

如果您需要任何进一步的帮助,请告诉我。

【讨论】:

  • 等等,我认为 React 中“提升状态”的全部意义在于父组件是子组件的唯一真实来源?因此,父级将数据传递给其子级,并在更新时重新渲染。
  • 我不同意我认为将引用从父组件传递到子组件是一种反模式,应该避免
  • 我的措辞不佳,我已经为您编辑了答案。我并不是说提升状态是反模式,我的意思是您将 this.state.form 向下传递给子组件并尝试直接在子组件中修改父状态的值。
  • @ravibagul91 你说的都是对的,我不是这个意思。我的措辞很糟糕,哈哈。
  • @DanielPark 我了解您的代码 sn-p 提供了另一种解决方案(使用 useState 和挂钩),但我不确定如何将其转换为我提供的结构。当然我的结构可能完全错误,但这就是为什么我要问哈哈!
猜你喜欢
  • 2017-07-09
  • 2020-01-23
  • 1970-01-01
  • 1970-01-01
  • 2020-07-05
  • 2020-11-16
  • 2018-01-27
  • 2021-03-03
  • 1970-01-01
相关资源
最近更新 更多