无法在React中将值设置为只读属性

时间:2019-08-29 09:29:10

标签: javascript reactjs

我有两个不同的单选按钮,它们会在onChange上触发功能。 我有两个属性:selectedLabel和selectedTestdata。 我想在触发函数时更改这些属性的值。

我收到一条错误消息,提示我无法更改只读属性。

这是我的代码:

import React, { useState, Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import './styles.css'
import { bindActionCreators } from 'redux';
import PropTypes from 'prop-types';
import { testdataRequest } from '../../actions/testdata'
import { labelsRequest } from '../../actions/labels'
....

export class TemplateDetails extends Component {

    componentDidMount() {
        this.props.testdataRequestConnect(this.props.match.params.templateid, this.props.match.params.locale);
        this.props.labelsRequestConnect(this.props.match.params.templateid);
    }

    testDataReloader = (e) => {
        this.props.selectedTestdata = e.target.value;
        console.log(e.target.value);
    }

    labelsReloader = event => {
        console.log(JSON.parse(event.target.value));
    }
    

    renderTestdata() {
        const { testdata } = this.props;
        if (!testdata.length) {
            return (
                <span
                    data-at="no-customers"
                    className="pos-v-center pos-left pos-right pos-absolute t-center t-light-grey">
                    <span className="d-block t-1 t-bold">No testdata found</span>
                </span>
            );
        }

        return (
            <div
                style={{ flexGrow: '1', flexShrink: '1' }}
                className="t-left ox-hidden oy-hidden"
                data-at="customer-search-list">
                {testdata.map((data, index) => (
                    <div key={data}>
                        <div className="form-element type-radio">
                            <span className="box-toggler">
                                <input onChange={this.testDataReloader} value={data} type="radio" name="radio-testdata" className="form-input" id={index}/>
                                <span className="indicator"></span>
                            </span>
                            <label htmlFor={index} className="form-placeholder">{data}</label>
                            <span className="icon icon--sm pos-absolute pos-right pos-v-center">
                                <svg title="icon--arrow-small-right" role="img">
                                    <use xmlnsXlink="http://www.w3.org/1999/xlink" xlinkHref="#icon--arrow-small-right" />
                                </svg>
                            </span>
                        </div>
                    </div>
                ))}
            </div>
        );
    }

    renderLabels() {
        const { labels } = this.props;
        if (!labels.length) {
            return (
                <span
                    data-at="no-customers"
                    className="pos-v-center pos-left pos-right pos-absolute t-center t-light-grey">
                    <span className="d-block t-1 t-bold">No labels found</span>
                </span>
            );
        }

        return (
            <div
                style={{ flexGrow: '1', flexShrink: '1' }}
                className="t-left"
                data-at="customer-search-list">
                {labels.map((data, index) => (
                    <div key={data.uuid}>
                        <div className="form-element type-radio">
                            <span className="box-toggler">
                                <input onChange={this.labelsReloader} value={JSON.stringify(data)} type="radio" name="radio-labels" className="form-input" id={data.uuid}/>
                                <span className="indicator"></span>
                            </span>
                            <label htmlFor={data.uuid} className="form-placeholder">{data.values.creationTime}</label>
                            <span className="icon icon--sm pos-absolute pos-right pos-v-center">
                                <svg title="icon--arrow-small-right" role="img">
                                    <use xmlnsXlink="http://www.w3.org/1999/xlink" xlinkHref="#icon--arrow-small-right" />
                                </svg>
                            </span>
                        </div>
                    </div>
                ))}
            </div>
        );
    }

    render() {
        const { isFetchingTestdata, errorTestdata, isFetchingLabels, errorLabels } = this.props;
        return (
            <div className="pos-relative h-100 o-hidden">
                <div className="d-flex h-100 w-100 pos-relative">
                    <div className="col-4 p-0">
                        <div className="d-flex flex-column h-100 pl-4 pb-4">
                            <div
                                className="px-2 mt-4 w-100"
                                style={{ maxWidth: '500px' }}>
                                <Link to="/templates">
                                    <Button variant="link">
                                        <span className="t-1" style={{ lineHeight: '1' }}>&lsaquo;</span> Templates
                                    </Button>
                                </Link>
                            </div>

                            {/* Test data */}
                            <div className="d-flex flex-wrap align-content-start py-2 pos-relative bg-white shadow t-center mt-4 p-4 w-100 h-100" style={{ flexGrow: '1', overflowY: 'scroll' }}>
                                <div className="w-100" style={{ height: 'fit-content' }}>
                                    {errorTestdata && <Alert variant="error">{errorTestdata}</Alert>}
                                    <h1>Test data</h1>
                                    <p className="t-grey mb-4">
                                        Available testdata
                                        <span className="d-block">Select one to render the template with it</span>
                                    </p>
                                </div>
                                {isFetchingTestdata ? <span className="throbber" /> : this.renderTestdata()}
                            </div>

                            {/* Labels */}
                            <div className="d-flex flex-wrap py-2 pos-relative bg-white shadow t-center mt-4 p-4 w-100 h-100" style={{ flexGrow: '1', overflowY: 'scroll' }}>
                                <div className="w-100">
                                    <h1>Labels</h1>
                                    <p className="t-grey mb-4">
                                        {errorLabels && <Alert variant="error">{errorLabels}</Alert>}
                                        Available labels
                                        <span className="d-block">Select one to render the template with them</span>
                                    </p>
                                </div>
                                {isFetchingLabels ? <span className="throbber" /> : this.renderLabels()}
                            </div>

                        </div>
                    </div>
                    <div className="col-8 mr-4">
                        <div className="d-flex flex-column h-100 pl-4 pb-4 pr-4">
                            <div className="d-flex flex-column bg-white shadow t-left p-4 mt-4 w-100 h-100" style={{ minHeight: '20px', maxHeight: '70px' }}>
                                <div className="w-100 h-100 mb-2">
                                    <h1 className="mb-2">Subject </h1>
                                </div>
                            </div>
                            <div className="d-flex flex-column bg-white shadow t-center p-4 mt-4 w-100 h-100">
                                <div>
                                    <h1>Template</h1>
                                    <p className="t-grey mb-4">
                                        Live preview of the template
                                    </p>
                                </div>
                            </div>
                        </div>

                    </div>
                </div>
            </div>


        );
    }
}

TemplateDetails.propTypes = {
    isFetchingTestdata: PropTypes.bool,
    isFetchingLabels: PropTypes.bool,
    errorTestdata: PropTypes.string,
    errorLabels: PropTypes.string,
    selectedLabel: PropTypes.string,
    selectedTestdata: PropTypes.string,
    templatesRequestConnect: PropTypes.func,
    labelsRequestConnect: PropTypes.func,
    testdata: PropTypes.array,
    labels: PropTypes.array,
};

TemplateDetails.defaultProps = {
    isFetchingTestdata: false,
    isFetchingLabels: false,
    errorTestdata: '',
    errorLabel: '',
    selectedLabel: '',
    selectedTestdata: '',
    templatesRequestConnect: () => { },
    labelsRequestConnect: () => { },
    testdata: [],
    labels: [],
};

export default connect(
    (state) => ({
        isFetchingTestdata: state.testdataStore.isFetching,
        errorTestdata: state.testdataStore.error,
        testdata: state.testdataStore.testdata,
        isFetchingLabels: state.labelsStore.isFetching,
        errorLabels: state.labelsStore.error,
        labels: state.labelsStore.labels,
    }),
    (dispatch) => bindActionCreators({
        testdataRequestConnect: testdataRequest,
        labelsRequestConnect: labelsRequest,
    }, dispatch),
)(TemplateDetails);

2 个答案:

答案 0 :(得分:3)

props are read-only,而您尝试分配给它:

testDataReloader = e => {
  // this.props.selectedTestdata = e.target.value;
  console.log(e.target.value);
};

关于为什么不能更改道具on Stack Overflow,您有很多类似的讨论。

答案 1 :(得分:0)

您不能直接更改道具,因为道具是只读的。为了获得结果,您采用了不同的方法。

您可以做的一件事情是,可以传递selectedTestdata,而可以通过父组件传递一个方法作为道具,该道具将负责更改您的父状态值。

例如 在父组件中

<Child selectedTestdata={this.state.selectedTestdata} udpatedTestData={this.updateTestData}/>

和父组件,您可以定义方法updateTestData,该方法将更改selectedTestdata的值。

在您的Child组件中,您可以调用此prop方法

testDataReloader = (e) => {
    this.props.updateTestData(e.target.value);
    console.log(e.target.value);
}