【问题标题】:Warning Controlled Component Changing to Uncontrolled警告受控组件更改为不受控
【发布时间】:2019-06-03 18:53:30
【问题描述】:

我有两个隐藏的输入字段,当节点提取完成并且值设置没有任何问题时,它们正在填充来自道具的值,但我看到以下警告。

Warning: A component is changing a controlled input of type hidden to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

虽然我了解受控和不受控之间的区别,但我似乎无法理解为什么我的组件会在两者之间造成冲突。我的组件代码中是否有任何明确的内容可能导致此问题?

import React from 'react';
import isEqual from 'lodash/isEqual';

export default class DateFilter extends React.Component {
    constructor(props) {
        super(props);
        this.state = { 
            startDateValue: '',
            endDateValue: ''
        };
    }

    componentDidMount() {
        this.setState({
            startDateValue: this.props.startDateQuery,
            endDateValue: this.props.endDateQuery
        });
    }

    handleChange(input, value) {
        this.setState({
            [input]: value
        })
    }

    componentWillReceiveProps(nextProps) {
        if (!isEqual(this.props, nextProps)){
            this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
        }
    }

    render() {
        return (
            <div className="col-md-3">
                <input type="hidden" name="_csrf" value={this.props.csrf} />
                <div className="input-group blog-filter-date-range-picker">
                    <p>Blog Date Range:</p>
                </div>
                <div className="input-group blogFilterDatePicker">
                    <span className="input-group-addon"><i className="glyphicon glyphicon-calendar"></i></span>
                    <input type="text" name="blogDateRange" className="form-control blogFilterDatePicker" autoComplete="off" />
                </div>
                <input type="hidden" name="blogStartDate" className="form-control" value={this.state.startDateValue} onChange={e => this.handleChange('startDateValue', e.target.value)} />
                <input type="hidden" name="blogEndDate" className="form-control" value={this.state.endDateValue} onChange={e => this.handleChange('endDateValue', e.target.value)} />
            </div>
        );
    }
}

父组件:

import React from 'react';

import CatgoryFilter from './SearchFormFilters/CategoryFilter';
import DepartmentFilter from './SearchFormFilters/DepartmentFilter';
import TeamFilter from './SearchFormFilters/TeamFilter';
import TypeFilter from './SearchFormFilters/TypeFilter';
import DateFilter from './SearchFormFilters/DateFilter';

//Activity Feed - Search Form
export default class ActivityFeedSearchForm extends React.Component {
    render() {
        var clearFilters;
        if(this.typeQuery || this.props.categoryQuery || this.props.departmentQuery || this.props.teamQuery || this.props.startDateQuery || this.props.endDateQuery || this.props.typeQuery){
            clearFilters = <a href="/app" id="clear-filter">Clear</a>;
        }

        return (
            <div className="row">
                <div className="annotation-search-form col-md-10 col-md-offset-1">
                    <div clas="row">
                        <form action="/app" method="post" className="annotation-filter-fields">
                            <DateFilter csrf={this.props.csrf} startDateQuery={this.props.startDateQuery} endDateQuery={this.props.endDateQuery} />
                            <TypeFilter typeQuery={this.props.typeQuery} />
                            <CatgoryFilter category={this.props.category} categoryQuery={this.props.categoryQuery} />
                            <DepartmentFilter department={this.props.department} departmentQuery={this.props.departmentQuery} />
                            <TeamFilter team={this.props.team} teamQuery={this.props.teamQuery} />
                            <div className="col-md-1 annotation-filter-section filter-button-container">
                                <button type="submit" id="annotation-filter-submit">Filter</button>
                                {clearFilters}
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        )
    }
}

顶级父组件:

import React from 'react';
import fetch from 'node-fetch';
import ReactMarkdown from 'react-markdown';
import path from 'path';
import ActivityFeedSearchForm from './ActivityFeedSearchForm/ActivityFeedSearchForm';
import { API_ROOT } from '../config/api-config';

//GET /api/test and set to state
export default class ActivityFeed extends React.Component{
    constructor(props, context) {
        super(props, context);
        this.state = this.context.data || window.__INITIAL_STATE__ || { team: [] };
    }

    fetchList() {
            fetch(`${API_ROOT}` + '/api' + window.location.search, { compress: false })
                .then(res => {
                    return res.json();
                })  
                .then(data => {
                    this.setState({ 
                        startDateQuery: data.startDateQuery,
                        endDateQuery: data.endDateQuery,
                    });
                }) 
                .catch(err => {
                    console.log(err);
                });
        }

    componentDidMount() {
        this.fetchList();
    }

    render() {  
            return (
                <div>
                    <Navigation notifications={this.state.notifications}/>
                    <ActivityFeedSearchForm csrf={this.state.csrf} category={this.state.category} categoryQuery={this.state.categoryQuery} department={this.state.department} departmentQuery={this.state.departmentQuery} team={this.state.team} teamQuery={this.state.teamQuery} typeQuery={this.state.typeQuery} startDateQuery={this.state.startDateQuery} endDateQuery={this.state.endDateQuery} />
                    <div className="activity-feed-container">
                        <div className="container">
                            <OnboardingInformation onboarding={this.state.onboardingWelcome} />
                            <LoadingIndicator loading={this.state.isLoading} />
                            <ActivityFeedLayout {...this.state} />
                        </div>
                    </div>
                </div>
            )
    }
};

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    由于您只处理 componentWillReceiveProps 中的 startDateValue 和 endDateValue,因此最好单独比较它们而不是整个 props。整个道具没有进行深度相等检查,这就是您收到警告的原因

    改变

        componentWillReceiveProps(nextProps) {
        if (!isEqual(this.props, nextProps)){
            this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
        }
    }
    

        componentWillReceiveProps(nextProps) {
        if (this.props.startDateQuery != nextProps.startDateQuery && this.props.endDateQuery != nextProps.endDateQuery){
            this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
        }
    }
    

    另外,您不需要 componentDidMount,因此请删除代码中的该部分并使用以下代码更新构造函数代码

         constructor(props) {
             super(props);
             this.state = { 
                  startDateValue: this.props.startDateQuery ? this.props.startDateQuery: '',
                   endDateValue:this.props.endDateQuery ? this.props.endDateQuery:  ''
             };
            }
        }
    

    还有

    改变

         <input type="hidden" name="_csrf" value={this.props.csrf} />
    

          <input type="hidden" name="_csrf" value={this.props.csrf ? this.props.csrf : "" />
    

    而且你没有绑定handleChange,所以要么在构造函数中手动绑定它,要么将其更改为如下箭头函数

    改变

        handleChange(input, value) {
        this.setState({
            [input]: value
        })
    }
    

        handleChange = (input, value) => {
        this.setState({
            [input]: value
        })
    }
    

    【讨论】:

    • 感谢您的回答。我更新了我的代码,但我仍然看到错误。是否有其他元素或功能可能导致此错误?
    • 这个 DateFilter 是不是任何父组件的子组件?
    • 是的,我更新了我的问题以包含父组件。我也有 jquery 使用日历覆盖更新 name="annotationDateRange" 输入逻辑,我很好奇这是否会产生任何影响
    • 您确定在 DateFilter 组件中收到该警告吗?
    • 是的,它是说警告来自in input (created by DateFilter) in div (created by DateFilter) in DateFilter (created by ActivityFeedSearchForm) in form (created by ActivityFeedSearchForm)
    猜你喜欢
    • 1970-01-01
    • 2021-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 2020-08-06
    • 2017-09-23
    • 2018-11-15
    相关资源
    最近更新 更多