I wanted to create the effect of editing the name of an item from the list. To do this, I passed the current name of the list item to the component where I have an input form and set state in this component to
state = {
nodeName: this.props.nodeName
}
In turn, I passed nodeName to input value. However, it turned out that assigning propertie to nodeName is not working because this.props.nodeName is empty. When I call in the render function - console.log (this.props.nodeName), the name shows up correctly
Here is the class where I pass my list item name(nodeName={this.state.activeNodeName}
) to EditNodeModal component:
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}
And my EditNodeModal 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 {
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}
I tried set state in the componentDidMount function but it didn't help.
Here is the whole code - https://codesandbox.io/s/long-http-jvt9c
Your example doesn't use the correct way to define the state in class-based components. Please check out the example below. state
should be defined as part of the class constructor.
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 };