草庐IT

javascript - React JS this.props.data 未在子组件的 getInitialState 中定义,即使它是在渲染中定义的(并且在父级中定义)

coder 2025-03-15 原文

场景:

  1. 在同一个祖父包装器 (<Row /> -> <Cell />) 中重用组件 (<ShippingTable /> -> <Grid />) 以实现代码重用。
  2. 首先,我分配了一个数据数组并循环以重新使用子组件 ( <Row /> -> <Cell /> )。
  3. 对于第二个,它只是一个对象(具有与数组对象相同的属性),我直接在渲染中分配它(不需要 this.props.data.map 循环,因为已经只有一个对象)。

问题:

  1. 对于阵列,一切都按要求工作。 this.props.data传递给 child ,状态通过各种事件更新,一切都很好。
  2. 然而,对于单个对象,在 <Row /> 之前一切正常.即使 this.props.data 包含有效值并正确分配给 child <Cell />组件,在 <Cell />的 getInitialState,它莫名其妙地未定义(或设置为 <ShippingTable />getInitialState 中设置的任何初始值)。虽然,除了在 .map 循环中被调用之外,它是完全相同的代码和数据,用于我的数据数组的相同渲染
  3. 此外,this.props.data<Cell />的呈现确实存在且准确,但由于 getInitialState 中的失败 setState,this.state.data 未定义(或设置为 <ShippingTable />getInitialState 中设置的任何值)。
  4. 此外,如果我强制重新呈现 UI(例如,更改祖 parent <ShippingTable /> 中的另一个状态属性),一切都会按照我最初的预期进行。

就好像getInitialState<Cell />因为只有我的单个百分比对象在 setState 的 AJAX 成功填充之前被调用,或者在使用来自服务器的更新数据填充后状态发生更改时不会再次调用它。

这是实际代码的精简版(但仍然很长):

更新 - 已解决

永远不要忘记使用 React,始终向下发送 props,向上发送事件,并始终将状态保持在尽可能远的顶部。已删除 <Cell /><Row />的状态跟踪责任,将它们移回 ShippingTable(而不是引用父级传递的 Prop ),其中状态实际被跟踪(并且应该始终被跟踪)。根据下面的@rallrall,我偏离了轨道,并且莫名其妙地与框架作对。回想起来一切都非常清楚(尽管为什么不正确的方法适用于数组而不适用于对象确实混淆了水域 - 特别是当将对象切换到数组时最终也有效)。

   var ShippingInput = React.createClass({
        getInitialState: function() {
            return { value: this.props.value };
        },
        handleChange: function(event) {
            var value = event.target.value;
            ...conversion to decimal and validation
            this.props.onChange(value);
            this.setState({ value: value });
        },
        render: function() {
            return (
                <Input type="text" placeholder="0.00" bsStyle={this.validationState()} addonBefore={addOnBefore}
                  value={this.state.value} label={this.props.label} ref="input" onChange={this.handleChange} 
                  groupClassName={this.props.groupClassName} labelClassName={this.props.labelClassName} onKeyDown={this.props.onKeyDown} />
        );
      }
    });

    var Cell = React.createClass({
        propTypes: {
            data: React.PropTypes.number.isRequired,
            onChange: React.PropTypes.func.isRequired,
            onRowEdit: React.PropTypes.func.isRequired
        },
        getInitialState: function() {
            // At this point, this.props.data is undefined *only for <Cell data={this.props.percentages} /> in <Row /> even though that prop is not null there.
            return {value: this.props.data};
        },
        handleChange: function(value) {
            this.props.onChange(value);
            this.setState({ value: value });
        },
        render: function() {
            var edit = this.props.edit;
            var showInput = edit ? 'group-class' : 'group-class hide'; 
            var showText = edit ? 'hide' : 'btn btn-default btn-sm';

            var val = this.props.isRates ? accounting.formatMoney(this.state.value) : this.state.value;

            // {this.state.value} is undefined here for only the percentages object
            // {this.props.data} is *not undefined* 
            var input = <ShippingInput type="text" label={this.props.label} value={this.state.value} ref="input"
                  isRates={this.props.isRates} groupClassName={showInput} labelClassName="label-class sr-only" onKeyDown={this.handleKeyDown} onChange={this.handleChange} />;

            var text = (<a href="#" className={showText} onClick={this.handleClick}>{val}</a>);

            return ( <td>{input}{text}</td> );
        }
    });

    var Row = React.createClass({
        propTypes: {
            data: React.PropTypes.object.isRequired,
            onCellChange: React.PropTypes.func.isRequired,
            onRowCommit: React.PropTypes.func.isRequired
        },
        getInitialState: function() {
            return {edit: false};
        },
        handleChange: function(prop, val) {
            this.props.onCellChange(prop, val);
        },
        ...
        render: function() {
            var edit = this.state.edit;
            var text = edit ? 'fa fa-save fa-fw' : 'fa fa-edit fa-fw';
            return <tr>
                <Cell data={this.props.data.Canada} isRates={this.props.isRates}  label="Canada" edit={edit} onRowEdit={this.handleRowEdit} onRowCommit={this.handleRowCommit}  onChange={this.handleChange.bind(null, "Canada")} />
                <Cell data={this.props.data.Us} isRates={this.props.isRates}  label="United States" edit={edit} onRowEdit={this.handleRowEdit}  onRowCommit={this.handleRowCommit}  onChange={this.handleChange.bind(null, "Us")} />
                <Cell data={this.props.data.International} isRates={this.props.isRates} label="International" edit={edit} onRowEdit={this.handleRowEdit}  onRowCommit={this.handleRowCommit} onChange={this.handleChange.bind(null, "International")} />
                <td>
                    <Button href="#" ref="commit" onClick={this.handleRowCommit} bsStyle="primary" bsSize="small"><span className={text}></span></Button>
                </td>
            </tr>;
        }
    });

    var Grid = React.createClass({
        propTypes: {
            data: React.PropTypes.array.isRequired,
            percentages: React.PropTypes.object.isRequired,
            onCellChange: React.PropTypes.func.isRequired,
            onRowCommit: React.PropTypes.func.isRequired
        },
        render: function() {
            var rows = this.props.data.map(function(rowData, index) {
                var id = rowData["Id"];
                return <Row key={id} isRates={true} data={rowData} onCellChange={this.props.onCellChange.bind(null, index)} onRowCommit={this.props.onRowCommit.bind(null, index)} onRowDelete={this.props.onRowDelete.bind(null, index)} />;
            }, this);

            return (
                <Table striped bordered hover responsive>
                  <thead>
                  <tr>
                    <th className="col-sm-4">Order Subtotal (up to)</th>
                    <th className="col-sm-2">Canada</th>
                    <th className="col-sm-2">US</th>
                    <th className="col-sm-2">International</th>
                    <th className="col-sm-1"></th>
                  </tr>
                  </thead>
                    <tbody>
                        {rows}
                        <tr><td colSpan="5">If the order subtotal is greater than the largest amount on the above chart, the following rates apply:</td></tr>
                        <Row key="Percentages" isRates={false} data={this.props.percentages} onCellChange={this.props.onPercentCellChange} onRowCommit={this.props.onPercentRowCommit} />
                    </tbody>
                </Table>  

            );
        }
    });

    var ShippingTable = React.createClass({
        getInitialState: function() {
            return this.props.initialData;
        },
        loadFromServer: function() {
            $.getJSON(this.props.url, function(data) {
                if (!data || data.Success === false) {
                    toastr.error('Error loading shipping costs. Please refresh the page and try again.');
                } else if (this.isMounted()) {
                    // This change is not reflected in Row/Cell for this.state/props.percentages until after force a UI update (via handleAdd and handleCancel 
                    // where this.state.add changes) even though a) percentages (a single object) holds valid values here and does all the way down to <Row />
                    // and b) there is no similar issue with this.state.data.
                    this.setState({ data: data.Value.ShippingCostMatrix, percentages: data.Value.ShippingPercentage });
                }
            }.bind(this));
        },
        componentDidMount: function() {
            this.loadFromServer();
        },
        handleCellChange: function(rowIdx, prop, val) {
            var row = copy(this.state.data[rowIdx]);
            row[prop] = val;
            var rows = this.state.data.slice();
            rows[rowIdx] = row;
            rows.sort(sortBySubtotal);
            this.setState({data: rows});
        },
        handlePercentCellChange: function(prop, val) {
            var row = copy(this.state.percentages);
            row[prop] = val;
            this.setState({percentages: row});
        },
        handleAdd: function(event) {
            event.preventDefault();
            this.setState({ add: true});
        },
        handleAddCancel: function(event) {
            event.preventDefault();
            this.setState({ add: false});
        },
        render: function() {
            var ctrl;
            if (this.state.add) {
                ctrl = (<NewRow onAddCancel={this.handleAddCancel} onRowAdd={this.handleRowAdd} />);
            }
            else {
                ctrl = (
                    <div>
                    <p><a onClick={this.handleAdd} className="btn btn-primary btn-lg">Add</a></p>
                    <Grid data={this.state.data} percentages={this.state.percentages}
                         onCellChange={this.handleCellChange} onPercentCellChange={this.handlePercentCellChange} onRowCommit={this.handleRowCommit}  onPercentRowCommit={this.handlePercentRowCommit} onRowDelete={this.handleRowDelete} />
                    </div>
                 );
            }

            return <div>{ctrl}</div>;
        }
    });

    //React.render(<ShippingTable initialData={ {data : [], percentages: { Canada: 1, Us: 1.25, International: 2.25, Id: 1 }, add: false} } 
    React.render(<ShippingTable initialData={ {data : [], percentages : {}, add: false} } 
        url="/admin/shipping/costs" update="/admin/shipping/update" create="/admin/shipping/create" delete="/admin/shipping/delete" updatePercentage="/admin/shipping/updatepercentage" />, document.getElementById('shipTable'));

最佳答案

getInitialState 应该返回初始组件状态,而不考虑 props。如果你真的想在状态中设置一个 prop 值,你应该使用 componentWillMount 钩子(Hook)。参见 docs .

尽管您在这里似乎是在与框架作对。当你想改变一个 prop 值时,父组件应该对此使用react,并为子组件提供新的 props。

关于javascript - React JS this.props.data 未在子组件的 getInitialState 中定义,即使它是在渲染中定义的(并且在父级中定义),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28996313/

有关javascript - React JS this.props.data 未在子组件的 getInitialState 中定义,即使它是在渲染中定义的(并且在父级中定义)的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  3. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  4. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  5. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  6. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  7. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  8. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  9. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  10. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

随机推荐