警告控制组件更改为不受控制

时间:2019-01-09 02:57:10

标签: reactjs

我有两个隐藏的输入字段,它们在节点获取完成且设置值时没有任何问题时,使用prop中的值进行填充,但是我看到以下警告。

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>
            )
    }
};

1 个答案:

答案 0 :(得分:1)

当您只处理componentWillReceiveProps中的startDateValue和endDateValue时,最好将它们而不是整个道具进行比较。整个道具都没有进行深度平等检查,这就是为什么您会收到警告

更改

    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
    })
}