Передача состояния в состояние в другом компоненте - React - PullRequest
0 голосов
/ 27 мая 2020

Я хотел создать эффект редактирования названия элемента из списка. Для этого я передал текущее имя элемента списка компоненту, в котором у меня есть форма ввода, и установил состояние в этом компоненте на

state = {
        nodeName: this.props.nodeName
    }

. В свою очередь, я передал nodeName входному значению. Однако оказалось, что присвоение свойства ie параметру nodeName не работает, поскольку this.props.nodeName пуст. Когда я вызываю функцию рендеринга - console.log (this.props.nodeName), имя отображается правильно

Вот класс, в котором я передаю имя элемента списка (nodeName={this.state.activeNodeName}) компоненту EditNodeModal:

import {treeActions} from '../_actions'
import { connect } from 'react-redux';
import Node from './Node/Node'
import './style.css'
import ContextMenu from './ContextMenu'
import {AddNodeModal} from './Modals/AddNodeModal'
import { EditNodeModal } from './Modals/EditNodeModal';

class TreePage extends Component {

    state = {
        activeNode: 0,
        activeNodeName: '',
        showContextMenu: false,
        top:0,
        left:0,
        showAddNodeModal: false,
        showEditNodeModal: false
      }

    componentDidMount(){
      //document.addEventListener('click', this.hideContextMenu.bind(this))
      this.props.getTree()
    }
    componentWillUnmount(){
      //document.removeEventListener('click',this.hideContextMenu.bind(this))
    }
    hideContextMenu(){
      this.setState({showContextMenu: false})
    }
    setActiveNode(id) {   
        this.setState({activeNode: id})
    }
    handleOnContextMenu(event, id, name){
        event.preventDefault()

        this.setState({activeNode: id, activeNodeName: name, showContextMenu: true, left: event.clientX, top: event.clientY})   
    }
    toggleShowAddNodeModal() {
      this.setState({showAddNodeModal: !this.state.showAddNodeModal})
    }
    toggleShowEditNodeModal() {
      this.setState({showEditNodeModal: !this.state.showEditNodeModal})
    }

    renderSubNodes(subNodes) {
        const {activeNode} = this.state
        return (
          <ul>
            {subNodes.map((node) => (
                <React.Fragment>
                    <li>
                        <div onContextMenu={(event) => this.handleOnContextMenu(event, node.nodeId, node.name)} className={activeNode===node.nodeId?"bgSelected":""} key={node.nodeId} onClick={() => this.setActiveNode(node.nodeId)}>
                            <Node name={node.name}/>
                        </div>
                        {node.subNodes.length > 0 && this.renderSubNodes(node.subNodes)}
                    </li>
                </React.Fragment>
            ))}
          </ul>
        );
      }

    render() {
        const tree2 = this.props.tree.items;

        return(
            <React.Fragment>
                {tree2 && this.renderSubNodes(tree2)}
                {this.state.showContextMenu? <ContextMenu  showAddNodeModal={() => this.toggleShowAddNodeModal()} showEditNodeModal={() => this.toggleShowEditNodeModal()} left={this.state.left} top={this.state.top}/>:''}

                <AddNodeModal
                  show={this.state.showAddNodeModal} 
                  onHide={() => this.toggleShowAddNodeModal()}
                  parentNodeId={this.state.activeNode}
                />

                <EditNodeModal
                  show={this.state.showEditNodeModal}
                  onHide={() => this.toggleShowEditNodeModal()}
                  nodeId={this.state.activeNode}
                  nodeName={this.state.activeNodeName}
                />
            </React.Fragment>
        )
    }
}
function mapState(state){


    return state;
}
const actionCreators = {
    getTree: treeActions.getTree
}

const connectedApp = connect(mapState, actionCreators)(TreePage)
export {connectedApp as TreePage}

И мой компонент EditNodeModal:

import React,{Component} from 'react'
import {Modal, Button, Form} from 'react-bootstrap'
import {nodeActions} from '../../_actions'
import { connect } from 'react-redux';

class EditNodeModal extends Component {

    state = {
        nodeName: this.props.nodeName
    }

    handleChange({target}) {
        this.setState({nodeName: target.value})
    }
    editNode() {
        const {editNode} = this.props
        editNode(this.props.nodeId, this.state.nodeName)
        this.hideAndClearName()
    }
    hideAndClearName() {
        this.props.onHide()
        this.setState({nodeName:''})
    }

    render() {
        console.log(this.props.nodeName)
    return (
        <Modal
            onHide={() => this.hideAndClearName()}
            show={this.props.show}
            size="md"
            aria-labelledby="contained-modal-title-vcenter"
            centered
        >
        <Modal.Header closeButton>
            <Modal.Title id="contained-modal-title-vcenter">
                Edytuj nazwę
            </Modal.Title>
        </Modal.Header>
        <Modal.Body>
            <Form.Control value={this.state.nodeName} onChange={(event) => this.handleChange(event)} type="text" placeholder="Nazwa" />
        </Modal.Body>
        <Modal.Footer>
            <Button onClick={() => this.editNode()} variant="success">Edytuj</Button>
            <Button onClick={() => this.hideAndClearName()}>Zamknij</Button>
        </Modal.Footer>
        </Modal>
    );
    }
}

function mapState(state){

  return state;
}
const actionCreators = {
  editNode: nodeActions.editNode,

}

const connectedApp = connect(mapState, actionCreators)(EditNodeModal)
export {connectedApp as EditNodeModal}

Я попытался установить состояние в функции componentDidMount, но это не помогло.

Вот весь код - https://codesandbox.io/s/long-http-jvt9c

1 Ответ

1 голос
/ 27 мая 2020

В вашем примере не используется правильный способ определения состояния в компонентах на основе классов. Пожалуйста, ознакомьтесь с приведенным ниже примером. state должно быть определено как часть конструктора класса.

Treepage Component

import React, { Component } from "react";
import { treeActions } from "../_actions";
import { connect } from "react-redux";
import Node from "./Node/Node";
import "./style.css";
import ContextMenu from "./ContextMenu";
import { AddNodeModal } from "./Modals/AddNodeModal";
import { EditNodeModal } from "./Modals/EditNodeModal";

class TreePage extends Component {
  // Added this line
  constructor(props) {
    super(props);
    this.state = {
      activeNode: 0,
      activeNodeName: "",
      showContextMenu: false,
      top: 0,
      left: 0,
      showAddNodeModal: false,
      showEditNodeModal: false
    };

    // You have not bound your event handlers
    // Added event handler bindings
    this.toggleShowAddNodeModal = this.toggleShowAddNodeModal.bind(this);
    this.toggleShowEditNodeModal = this.toggleShowEditNodeModal.bind(this);
    this.handleOnContextMenu = this.handleOnContextMenu.bind(this);
  }

  componentDidMount() {
    document.addEventListener("click", this.hideContextMenu.bind(this));
    this.props.getTree();
  }

  componentWillUnmount() {
    document.removeEventListener("click", this.hideContextMenu.bind(this));
  }

  hideContextMenu() {
    this.setState({ showContextMenu: false });
  }

  setActiveNode(id) {
    this.setState({ activeNode: id });
  }

  handleOnContextMenu(event, id, name) {
    event.preventDefault();

    this.setState({
      activeNode: id,
      activeNodeName: name,
      showContextMenu: true,
      left: event.clientX,
      top: event.clientY
    });
  }

  toggleShowAddNodeModal() {
    this.setState({ showAddNodeModal: !this.state.showAddNodeModal });
  }

  toggleShowEditNodeModal() {
    this.setState({ showEditNodeModal: !this.state.showEditNodeModal });
  }

  renderSubNodes(subNodes) {
    const { activeNode } = this.state;
    return (
      <ul>
        {subNodes.map(node => (
          <React.Fragment>
            <li>
              <div
                onContextMenu={event =>
                  this.handleOnContextMenu(event, node.nodeId, node.name)
                }
                className={activeNode === node.nodeId ? "bgSelected" : ""}
                key={node.nodeId}
                onClick={() => this.setActiveNode(node.nodeId)}
              >
                <Node name={node.name} />
              </div>
              {node.subNodes.length > 0 && this.renderSubNodes(node.subNodes)}
            </li>
          </React.Fragment>
        ))}
      </ul>
    );
  }

  render() {
    const tree2 = this.props.tree.items;
    const tree = [
      {
        nodeId: 1,
        name: "node1",
        subNodes: [
          {
            nodeId: 4,
            name: "node1-1",
            subNodes: [],
            subLeaves: []
          },
          {
            nodeId: 5,
            name: "node1-2",
            subNodes: [],
            subLeaves: []
          }
        ],
        subLeaves: []
      },
      {
        nodeId: 2,
        name: "node2",
        subNodes: [],
        subLeaves: []
      },
      {
        nodeId: 3,
        name: "node3",
        subNodes: [
          {
            nodeId: 6,
            name: "node3-1",
            subNodes: [
              {
                nodeId: 7,
                name: "node3-1-1",
                subNodes: [],
                subLeaves: []
              },
              {
                nodeId: 8,
                name: "node3-1-2",
                subNodes: [],
                subLeaves: []
              }
            ],
            subLeaves: []
          },
          {
            nodeId: 9,
            name: "node3-2",
            subNodes: [],
            subLeaves: []
          }
        ],
        subLeaves: []
      }
    ];
    return (
      <React.Fragment>
        {this.renderSubNodes(tree)}
        {this.state.showContextMenu ? (
          <ContextMenu
            showAddNodeModal={() => this.toggleShowAddNodeModal()}
            showEditNodeModal={() => this.toggleShowEditNodeModal()}
            left={this.state.left}
            top={this.state.top}
          />
        ) : (
          ""
        )}

        {/*  Conditionally rendering so that the component mounts only when needed*/}
        {this.state.showAddNodeModal && (
          <AddNodeModal
            show={this.state.showAddNodeModal}
            onHide={() => this.toggleShowAddNodeModal()}
            parentNodeId={this.state.activeNode}
          />
        )}

        {/*  Conditionally rendering so that the component mounts only when needed*/}
        {this.state.showEditNodeModal && (
          <EditNodeModal
            show={this.state.showEditNodeModal}
            onHide={() => this.toggleShowEditNodeModal()}
            nodeId={this.state.activeNode}
            nodeName={this.state.activeNodeName}
          />
        )}
      </React.Fragment>
    );
  }
}
function mapState(state) {
  return state;
}
const actionCreators = {
  getTree: treeActions.getTree
};

const connectedApp = connect(
  mapState,
  actionCreators
)(TreePage);
export { connectedApp as TreePage };

NodeModal Component

import React, { Component } from "react";
import { Modal, Button, Form } from "react-bootstrap";
import { nodeActions } from "../../_actions";
import { connect } from "react-redux";

class EditNodeModal extends Component {
  constructor(props) {
    super(props);

    this.state = {
      nodeName: this.props.nodeName
    };

    this.handleChange = this.handleChange.bind(this);
    this.hideAndClearName = this.hideAndClearName.bind(this);
    this.editNode = this.editNode.bind(this);
  }

  handleChange({ target }) {
    this.setState({ nodeName: target.value });
  }

  editNode() {
    const { editNode } = this.props;
    editNode(this.props.nodeId, this.state.nodeName);
    this.hideAndClearName();
  }

  hideAndClearName() {
    this.props.onHide();
    this.setState({ nodeName: "" });
  }

  render() {
    // console.log(this.props.nodeName);
    return (
      <Modal
        onHide={() => this.hideAndClearName()}
        show={this.props.show}
        size="md"
        aria-labelledby="contained-modal-title-vcenter"
        centered
      >
        <Modal.Header closeButton>
          <Modal.Title id="contained-modal-title-vcenter">
            Edit Name
          </Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <Form.Control
            value={this.state.nodeName}
            onChange={event => this.handleChange(event)}
            type="text"
            placeholder="Nazwa"
          />
        </Modal.Body>
        <Modal.Footer>
          <Button onClick={() => this.editNode()} variant="success">
            Edit
          </Button>
          <Button onClick={() => this.hideAndClearName()}>Close</Button>
        </Modal.Footer>
      </Modal>
    );
  }
}

function mapState(state) {
  return state;
}
const actionCreators = {
  editNode: nodeActions.editNode
};

const connectedApp = connect(
  mapState,
  actionCreators
)(EditNodeModal);
export { connectedApp as EditNodeModal };
...