从父级更新子级组件中的属性?

时间:2020-01-03 06:20:28

标签: html reactjs ref

我正在用存储信息的卡制作Trello板。 我有3个重要组成部分来进行此操作:

TrelloBoard.js-包含5个属于div的车道。它还存储所有卡。

Lanes.js-是仅在视觉上容纳卡片的div

Cards.js-包含信息,可以拖动到其他通道

在TrellBoard.js中,我的卡如下存储在状态内:

cards: [ [], [], [], [], []],

此刻,每个组件的状态都保持跟踪它在[0-4]中的哪一列以及[0+]列中的位置

当我将一张卡从一个车道拖到另一个车道时,Card.js中的onDragStart方法存储位置(列-车道X -以及列-位置Y < / strong>),然后一旦将其放到车道上,就会调用Lane.js中的onDrop(...)方法,该方法使TrelloBoard.js知道我正在使用 lane X 移动卡片位置Y ,到Z线的最后一个位置

在TrelloBoard中,我现在将卡从纸牌阵列中拼接起来,并通过transferCards(...)方法将其移动到另一个位置:

初始:

0 1 2 3 4

-----

一次移动

0-2 3 4

--1--

现在,如果我像这样将Card 2从第三列移动到第二列:

失败

0 2 1 3 4

-----

我的卡1内的状态认为它位于位置(1)而不是显示的位置(0)。这是因为我移动了卡2之后,第3列中的其余卡(即第一张图中的卡1)没有更新其状态,该状态包含其索引。

我本以为可以在TrelloBoard.js中管理有关卡的所有数据,但是一旦我集成了矿石的复杂数据,例如截止日期,描述,状态甚至是化身……它就变得太复杂了,无法在内部进行管理TrelloBoard.js,我更愿意管理每个单独组件中的所有数据。

所以目前我还很想弄清楚如何管理卡

我还不太熟悉React,但是我想我可以将引用附加到已创建的每张卡上,并从TrelloBoard.js中拼接和更新card []数组的方法中,我可以在每个Card中(在受影响的列中)调用每个方法,这些方法将在列中移动其位置/索引(例如,诸如shiftDown(){this.setState({index:this.index-1})})和从TrelloBoard.js调用它

TrelloBoard.js

    import React, { Component } from 'react';
    import './TrelloBoard.css';
    import Card from '../components/Lane/Card';
    import Lane from '../components/Lane/Lane';
    import Modal from '../components/UI/Modal/Modal';
    import Axios from 'axios';


    class TrelloBoard extends Component {
        constructor(props) {
            super(props);
            this.child = React.createRef();
        }

        state = {
            cardModal: false,
            modalTitle: "default",
            modalStatus: "default",
            modalStatusNumber: -1,
            cards: [ [], [], [], [], []], // this should actually be lanes - thought
            lanes: [],
            testInit: false

        }

        setModalTitle = (name) => {
            this.setState({modalTitle : name});
        }

        setModalStatus = (status) => {
            this.setState({modalStatus : status})
        }

        closeModalHandler = () => {
            this.setState({cardModal : false}); 
        }

        cardClickedHandler = (name, col) => {
            this.setModalTitle(name);
            this.setModalStatus(this.titles[col]);
            this.setState({modalStatusNumber: col})

            this.openModalHandler();
        }

        openModalHandler = () => {
            this.setState({cardModal : true}); 
        }



        titles = ['Open', 'In progress', 'To be tested', 'Re-opened', 'Closed'];
        colors = ['open', 'progress', 'test', 'reopened', '

        componentDidMount() {
            this.initCards();
        }

        initCards() {

            if (!this.state.testInit) {
                let initCards = [ [], [], [], [], []];
                let initLanes = [];
                let i = 0;
                let n = 0;

            for (const t of this.titles) {
                n = 0;
                const keyName = i+"-"+n;
                const card = (<Card clicked = {this.cardClickedHandler}
                                    ref={this.child}
                                    keyID = {keyName}
                                    column={i} >Column {i} card</Card>);

                        initCards[i].push(card);


                const lane = (<Lane color={this.colors[i]} columnNumber={i} 
                    title = {t} transfer={this.transferCards}>
                             {card}
                </Lane>);
                initLanes.push(lane);
                i++;

            }

            this.setState({ cards: [...initCards] });
            this.setState({ lanes: [...initLanes] });
            this.setState({ testInit: true});
        }

        }



        transferCards = (dragStartColumn, dragStartIndex, dragDestinationColumn, name) =>  {



            const tempCards = [...this.state.cards];

            // const spliced
            tempCards[dragStartColumn].splice(dragStartIndex, 1);
            // now all the cards after this one will have the incorrect position so we will have to update them all
            // but then I don't have access to the rest of the data in the lane after that... damn!

            const keyName = dragDestinationColumn+"-"+(tempCards[dragDestinationColumn].length);

            const newCard = (<Card clicked = {this.cardClickedHandler}
                keyID = {keyName} ref={this.child} index={tempCards[dragDestinationColumn].length-1}
                column={dragDestinationColumn} name={name}>{name}</Card>);



            tempCards[dragDestinationColumn].push(newCard);

            // THIS IS WHAT I HAVE IN MIND!
            // for (const c of tempCards[dragStartColumn]) {
            //     c.child.shiftDown();
            // }

            // alert("col: " + dragDestinationColumn + " | item: " + (tempCards[dragDestinationColumn].length-1) + 
            //     " | destCol: " + dragDestinationColumn);


            // spliced[0] needs to update its column index


            const tempLanes = [...this.state.lanes];

            const i = dragStartColumn;

            const originalLane = (<Lane color={this.colors[i]} columnNumber={i} 
                title = {this.titles[i]} transfer={this.transferCards}>
                         {tempCards[dragStartColumn]}
            </Lane>);

const d = dragDestinationColumn;

            const destinationLane = (<Lane color={this.colors[d]} columnNumber={d} 
                title = {this.titles[d]} transfer={this.transferCards}>
                        {tempCards[dragDestinationColumn]} 
            </Lane>);
            // problem 2 lines tempCards[..]..

            alert(tempCards[2].length);
            tempLanes[dragStartColumn] = originalLane;
            tempLanes[dragDestinationColumn] = destinationLane;

            this.setState( { cards: tempCards });
            this.setState( { lanes: tempLanes });

            // alert(array.length + " nuber of columns");

        }



        render () {



            return (

                    <div className="wrapper">
                        {this.state.lanes}
                <Modal 
                show = {this.state.cardModal} modalClosed = {this.closeModalHandler} 
                status={this.state.modalStatus} title={this.state.modalTitle}
                color={this.state.modalStatusNumber} >
                </Modal>



    </div>
            );
        }
    }

    export default TrelloBoard;

Lane.js

import React, { Component } from 'react';
import './Lane.css';
import LaneContents from './LaneContents';
import LaneTitle from './LaneTitle';
import Space from './Space';


 class Lane extends Component {


    state = {
        columnNumber: -1
    }

    onDragOver(ev) {
        ev.preventDefault();
    }

    onDrop(ev, cat)  {

        let id = ev.dataTransfer.getData("id");
        let name = ev.dataTransfer.getData("name");

        const draggedCardColumn = id.split("-");

        if (+draggedCardColumn[0] === this.state.columnNumber) {
            return alert("dragging and dropping to same col");
        }

        // let TrelloBoard know to remove the card and put it in the other column
        alert("col: " + +draggedCardColumn[0] + " | item: " + +draggedCardColumn[1] + " | destCol:" + +this.state.columnNumber);
        this.props.transfer(+draggedCardColumn[0], +draggedCardColumn[1], this.state.columnNumber, name);
    }

    componentDidMount() {
        this.setState({ columnNumber: this.props.columnNumber })
    }

     render() {

        return (
            <div className= "box" 
            onDrop={(e)=>{this.onDrop(e, "wip")}}
            onDragOver={(e) => this.onDragOver(e)}>
            <LaneTitle color={this.props.color}>{this.props.title}</LaneTitle>
                <Space/>
    <LaneContents>  
        {this.props.children}
     </LaneContents>
        </div>
    );
}
};

export default Lane;

Card.js

import React, { Component } from 'react';
import './Card.css';



class Card extends Component {
    state = {
        column: 2,
        index: -1,
        name: "default"
    }

    componentDidMount() {
        this.setState({column: this.props.column});
        this.setState({title: this.props.children});
        this.setState({index: this.props.index});
        this.setState({name: this.props.name});
    }

    onDragEnd = (ev) => {
        ev.preventDefault();
    }

    onDragStart = (ev, id) => {
        console.log('dragstart:',id);
        ev.dataTransfer.setData("id", id);
        ev.dataTransfer.setData("name", this.props.children);
    }

    updateIndex(newIndex) {
        this.setState({ index: newIndex });
    }

    shiftDown() {
        // THIS IS WHAT I HAVE IN MIND - to be called from TrelloBoard.js
        // this.setState({ index: this.state.index - 1});
    }

    updateColumn(newColumn) {
        this.setState({ column: newColumn });
    }

    setTitle() {
        this.setState({ title: this.props.children})
    }


    render() {

            return (
            <div 
            key = {this.props.keyID}
            className="box-contents--card droppable" 
            draggable="true" 
            onClick = {() => this.props.clicked(this.props.children, this.state.column) }
            onDragStart = {(e) => this.onDragStart(e, this.props.keyID)}
                onDragEnd = {(e) => this.onDragEnd(e)}
            >
        {this.props.children}
       </div>
    );
}
};

export default Card;

0 个答案:

没有答案