React 通过组件提升状态

React Lifting Sate Up Through Components

我无法解决这个问题。我正在编写一个仪表板应用程序以在 React 中使用。我试图让事情变得美好和模块化。我有两个布局 classes "row.js" & "col.js" 它们基本上返回 bootstrap 响应式网格元素,"col-sm-3",等等

我创建了一个小部件 class 和一个可扩展的小部件 class。小部件 class returns 具有某些样式的 div 和可扩展的小部件 class 扩展了小部件 class 并添加了一个点击处理程序以扩展到完整的浏览器宽度。然后我创建自定义小部件 classes 来扩展这些小部件 classes 中的任何一个。这些自定义 classes 将图形或表格等添加到实际组件中。我试图解决的问题是:

在我的 dashboard.jsx 我有:

<Row>
    <Col size="sm" num="3"> 
        <SpeakerFees expanded={false}/>
    </Col>
    <Col size="sm" num="9">
        <ProgramStatusByZoneOverview />
    </Col>
</Row>

我想单击小部件组件,将其传递给最近的祖先“”并提醒同一行中的其他小部件。根据反应提升状态文档,我们应该提升到最近的祖先……在这种情况下,两个元素向上。这很烦人哈哈

因为仪表板的布局最多只包含大约 10 个小部件,我认为最好是将它们硬编码在 dashboard.jsx 中,而不是使用某种循环。下面是我剩下的 classes.

dashboard.jsx

var React = require('react');
var ReactDOM = require('react-dom');

import { Row } from './layout/row.jsx';
import { Col } from './layout/col.jsx';

import { TotalWidget } from './widgets/total-widget.jsx';
import { ProgramsPlannedBySeries } from './widgets/programs-planned-by-series.jsx';
import { SpeakerFees } from './widgets/speaker-fees.jsx';
import { ProgramStatusByZoneOverview } from './widgets/program-status-by-zone-overview.jsx';

import 'bootstrap/dist/css/bootstrap.css';
import '../styles/app.scss';

class Dashboard extends React.Component {
    render() {
        var list = [
            {
                "size": "sm",
                "num": "3"
            },{
                "size": "sm",
                "num": "3"
            },{
                "size": "sm",
                "num": "3"
            },{
                "size": "sm",
                "num": "3"
            }
        ]
        return (
            <div className="container">
                <Row>
                    {list.map(function(object, i){
                        return (
                            <Col key={i.toString()} size={object.size} num={object.num}><TotalWidget key={i.toString()} /></Col>
                        );
                    })}
                </Row>
                <Row>
                    <Col size="sm" num="12">
                        <ProgramsPlannedBySeries />
                    </Col>
                </Row>
                <Row>
                    <Col size="sm" num="3"> 
                        <SpeakerFees expanded={false}/>
                    </Col>
                    <Col size="sm" num="9">
                        <ProgramStatusByZoneOverview />
                    </Col>
                </Row>
                <Row>
                    <Col size="sm" num="3"> 
                        <SpeakerFees expanded={false}/>
                    </Col>
                    <Col size="sm" num="9">
                        <ProgramStatusByZoneOverview />
                    </Col>
                </Row>
                <Row>
                    <Col size="sm" num="3"> 
                        <SpeakerFees expanded={false}/>
                    </Col>
                    <Col size="sm" num="9">
                        <ProgramStatusByZoneOverview />
                    </Col>
                </Row>
            </div>
        );
    }
}

ReactDOM.render(<Dashboard />, document.getElementById('widgets-wrapper')); 

和我的小部件 classes:

widget.jsx
var React = require('react');

import {TweenMax, Power2, TimelineLite} from "gsap";

export class WidgetMenu extends React.Component {
    constructor() {
        super();
        this.click = this.click.bind(this);
    }
    click() {
        alert()
    }
    render() {
        return (
            <span onClick={this.click} className={"glyphicon glyphicon-option-horizontal elipsis " + this.props.state} ></span>
        );
    }
}

export class Widget extends React.Component {
    constructor(props) {
        super(props);
        this.state = {'scale': false, 'class': ''};
        this.hover = this.hover.bind(this);
    }
    hover() {
        if(!this.state.scale){
            this.setState({'scale': true, 'class': 'scaleUp'})
        }else{
            this.setState({'scale': false, 'class': ''})
        }
    }
    click() {
        // alsert('widget')
    }
    render() {
        return (
              <div onClick={this.click} onMouseEnter={this.hover} onMouseLeave={this.hover} className="widget"><WidgetMenu state={this.state.class} />{ this.props.children }</div>
            )
    }
}

和小部件-expandable.jsx

widget-expandable.jsx
import { Widget, WidgetMenu } from './widget.jsx';
var React = require('react');

export class WidgetExpandable extends Widget {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div onMouseEnter={this.hover} onMouseLeave={this.hover} className="widget"><WidgetMenu state={this.state.class} />{ this.props.children }</div>
        )
    }

}

这里是两个彼此相邻的自定义小部件 cols/rows 在 dashboard.jsx 中:

speaker-fee.jsx
"use strict";
var React = require('react');
var Highcharts = require('highcharts');

import { WidgetExpandable } from './widget-expandable.jsx';

export class SpeakerFees extends React.Component {
    render() {
        return (
            <WidgetExpandable handleClick={this.props.handleClick}>
                <div id="speaker-fees-chart"></div>
            </WidgetExpandable>
        );
    } 
}

program-status.jsx
"use strict";
var React = require('react');
import { Widget } from './widget.jsx';

export class ProgramStatusByZoneOverview extends React.Component {

    render() {
        return (
            <Widget keyId={1}>
                <table className="table" id="speaker-fees-table">
                    <thead>
                        <tr>
                            <th>Speaker Fees, Travel...</th>
                            <th>Budgeted Spend</th>
                            <th>Actual Spend</th>
                            <th>Variance</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>Fee For Service</td>
                            <td>250,000</td>
                            <td>150,000</td>
                            <td>-1.5%</td>
                        </tr>
                        <tr>
                            <td>Fee For Service</td>
                            <td>250,000</td>
                            <td>150,000</td>
                            <td>-1.5%</td>
                        </tr>
                        <tr>
                            <td>Fee For Service</td>
                            <td>250,000</td>
                            <td>150,000</td>
                            <td>-1.5%</td>
                        </tr>
                        <tr>
                            <td>Fee For Service</td>
                            <td>250,000</td>
                            <td>150,000</td>
                            <td>-1.5%</td>
                        </tr>
                        <tr>
                            <td>Fee For Service</td>
                            <td>250,000</td>
                            <td>150,000</td>
                            <td>-1.5%</td>
                        </tr>
                    </tbody>
                </table>
            </Widget>
        );
    }
}

我想通了。关键是从我的 row.js class 开始。我在 row.js class 上创建了一个点击事件,在行 class 上设置了 expanded=false 状态。然后遍历每个 child 并将此点击处理程序添加为道具。然后添加这个prop作为children.

的onclick
export class Widget extends React.Component {
    constructor(props) {
        super(props);
        this.state = {'scale': false, 'class': ''};
        this.hover = this.hover.bind(this);
    }
    hover() {
        if(!this.state.scale){
            this.setState({'scale': true, 'class': 'scaleUp'})
        }else{
            this.setState({'scale': false, 'class': ''})
        }
    }

    render() {
        console.log(this.props.onExpandChange)
        return (
              <div onClick={this.props.onExpandChange} onMouseEnter={this.hover} onMouseLeave={this.hover} className="widget"><WidgetMenu state={this.state.class} />{ this.props.children }</div>
            )
    }
}

"use strict";
var React = require('react');

export class Col extends React.Component {
    render() {
        const childrenWithProps = React.Children.map(this.props.children,
                (child) => React.cloneElement(child, {
                    onExpandChange: this.props.onExpandChange
                })
            );
        return (
            <div  className={"col-" + this.props.size + '-' + this.props.num}>
                {childrenWithProps}
            </div>
        );
    }
}

"use strict";
var React = require('react');

export class Row extends React.Component {
    constructor(props) {
        super(props);
        this.state = {expanded: false}
        this.click = this.click.bind(this);
    }
    click() {
        this.setState({"expanded": !this.state.expanded});
        console.log(this.state.expanded);
    }
    render() {
        const childrenWithProps = React.Children.map(this.props.children,
                (child) => React.cloneElement(child, {
                    onExpandChange: this.click
                })
            );
        return (
            <div className="row">{ childrenWithProps }</div>
        )
    } 
}