0

My application have two components, one renders a list and the other render the form:

I would like to use the same component to create new categories and also to edit the categories that already exists.

The create categories is working fine already, but to edit, needs to pass the id of the category in the list to the form inside the modal, as i am new into react, i would like some help. Thanks in advance.

The list file is called,

Categories.jsx

import React, { Component } from 'react'
import { Alert, Modal, Button } from "react-bootstrap";
import Datatable from '../../../globalcomponents/datatable/Datatable';
import CategoryForm from './CategoryForm';

const Api = require('../../api/CategoriesApi.js')

class Categories extends Component {
  constructor(props) {
    super(props) 
    this.state = {
      categories: [],
      isLoaded: false,
      error: null,
      isOpen: false
    }
  }

  openModal = () => this.setState({ isOpen: true });
  closeModal = () => this.setState({ isOpen: false });

  componentDidMount() {
    Api.getCategories()
      .then(response => {
        const [error, data] = response
        if (error) {
          this.setState({
            isLoaded: true,
            categories: [],
            error: data
          })
        } else {
          this.setState({
            isLoaded: true,
            categories: data
          })
        }
      })
  } 

  render() {
    const { error, isLoaded, categories } = this.state

    if (error) {

      return (
        <Alert color="danger">
          Error: {error}
        </Alert>
      )

    } else if (!isLoaded) {

      return (
        <Alert color="primary">
          Loading...
        </Alert>
      )

    } else {

      return (
        <>            
            <Button className="float-right" variant="primary" onClick={this.openModal}>
              Adicionar
            </Button>
            
            <h4 className="mt-4 mb-4">Categorias de investimentos</h4>
            <Datatable>
              <table className="table table-striped my-4 w-100">
              <thead>
                <tr>
                  <th>ID</th>
                  <th>Title</th>
                  <th>Url (Slug)</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {categories.map(category => (
                  <tr key={category.id}>
                    <td>{category.id}</td>
                    <td>{category.title}</td>
                    <td>{category.slug}</td>
                    <td>
                    

                      <Button className="float-right mr-2" variant="primary" onClick={this.openModal}>
                        Modal Edit
                      </Button>

                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
            </Datatable>        
            
            <Modal show={this.state.isOpen} onHide={this.closeModal}>
              <Modal.Header closeButton>
                <Modal.Title>Adicionar / Editar</Modal.Title>
              </Modal.Header>
              <Modal.Body>
                <CategoryForm />
              </Modal.Body>
              <Modal.Footer>
                <Button variant="secondary" onClick={this.closeModal}>
                  Close
                </Button>
              </Modal.Footer>
            </Modal>
            </>



     )

    }

  }
}

export default Categories


The form file is used to create or edit categories. And it is called: CategoryForm.jsx

import React, { Component } from 'react'
import { Redirect } from 'react-router'
import { Row, Col, Alert, Button, Form, FormGroup, Label, Input } from 'reactstrap'

const Api = require('../../api/CategoriesApi.js')

class CategoryForm extends Component {
  constructor(props) {
    super(props)

    this.state = {
      category: {
        id: this.getCategoryId(props),
        title: '',
        slug: '',
      },
      redirect: null,
      errors: []
    }

    this.setTitle = this.setTitle.bind(this)
    this.setSlug = this.setSlug.bind(this)
    
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  getCategoryId(props) {
    try {
      return props.match.params.id
    } catch (error) {
      return null
    }
  }

  setTitle(event) {
    let newVal = event.target.value || ''
    this.setFieldState('title', newVal)
  }

  setSlug(event) {
    let newVal = event.target.value || ''
    this.setFieldState('slug', newVal)
  }

  setFieldState(field, newVal) {
    this.setState((prevState) => {
      let newState = prevState
      newState.category[field] = newVal
      return newState
    })
  }

  handleSubmit(event) {
    event.preventDefault()

    let category = {
      title: this.state.category.title,
      slug: this.state.category.slug,
    }

    Api.saveCategory(category, this.state.category.id)
      .then(response => {
        const [error, errors] = response
        if (error) {
          this.setState({
            errors: errors
          })
        } else {
          this.setState({
            // reload categories
            redirect: '/admin'
          })
        }
      })
  }

  componentDidMount() {
    if (this.state.category.id) {
      Api.getCategory(this.state.category.id)
        .then(response => {
          const [error, data] = response
          if (error) {
            this.setState({
              errors: data
            })
          } else {
            this.setState({
              category: data,
              errors: []
            })
          }
        })
    }
  }

  render() {
    const { redirect, category, errors } = this.state

    if (redirect) {
      return (
        <Redirect to={redirect} />
      )
    } else {

      return (
        <>
          <Row>
            <Col>
              {errors.length > 0 &&
                <div>
                  {errors.map((error, index) =>
                    <Alert color="danger" key={index}>
                      {error}
                    </Alert>
                  )}
                </div>
              }

              <Form onSubmit={this.handleSubmit}>
                <FormGroup>
                  <Label for="title">Title</Label>
                  <Input type="text" name="title" id="title" value={category.title} placeholder="Enter title" onChange={this.setTitle} />
                </FormGroup>
                <FormGroup>
                  <Label for="slug">Slug</Label>
                  <Input type="text" name="slug" id="slug" value={category.slug} placeholder="Enter slug" onChange={this.setSlug} />
                </FormGroup>
                <Button color="success">Submit</Button>
              </Form>
            </Col>
          </Row>
        </>
      )
    }
  }
}

export default CategoryForm



Preetham
  • 87
  • 3
Diogo Wernik
  • 586
  • 8
  • 24

2 Answers2

1

You are not passing the id in Categories.jsx. either you can set the id in the history state or do pass it by component prop drill.

setting the state in history Programmatically set params in React Router v4

Or You can do Pass the id to the Component and handle in Component DidMount Event.

here is the code sandbox link

Categories.jsx


/** Create a id variable in state. **/
class Categories extends Component {
                        constructor(props) {
                            super(props);
                            this.state = {
                                categories: [],
                                isLoaded: false,
                                error: null,
                                isOpen: false,
                               --> id: null <--
                            };
                        }
            
/** change the openModal code to something like this. **/
openModal = (id) => {
                  this.setState( (prev) => {
                      const state = prev.state;
                      return { ...state, id: id, isOpen:true };
                    });
                    };
    
                           
/** while Onclick set the id in the state. **/
<Datatable>
                    <table className="table table-striped my-4 w-100">
                      <thead>
                        <tr>
                          <th>ID</th>
                          <th>Title</th>
                          <th>Url (Slug)</th>
                          <th></th>
                        </tr>
                      </thead>
                      <tbody>
                        {categories.map((category) => (
                          <tr key={category.id}>
                            <td>{category.id}</td>
                            <td>{category.title}</td>
                            <td>{category.slug}</td>
                            <td>
                              <Button
                                className="float-right mr-2"
                                variant="primary"
                             -->   onClick={() =>this.openModal(category.id)}
                              >
                                Modal Edit
                              </Button>
                            </td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </Datatable>
               
             
/** Pass the id prop for CategoryForm Component in Modal body from the state. **/

 <Modal show={this.state.isOpen} onHide={this.closeModal} >
                              <Modal.Header closeButton>
                                <Modal.Title>Adicionar / Editar</Modal.Title>
                              </Modal.Header>
                              <Modal.Body>
                              -->  <CategoryForm id={this.state.id || null} />
                              </Modal.Body>
                              <Modal.Footer>
                                <Button variant="secondary" onClick=  {this.closeModal}>
                                  Close
                                </Button>
                              </Modal.Footer>
                            </Moddal>

CategoryForm.jsx

In the componentDidMount conditionally check if there is id variable in props.


  componentDidMount() {
    **// Check if props.id is available** 
        if (** this.state.category.id || this.props.id **) {
              **const id = this.state.category.id || this.props.id;**
                Api.getCategory(id).then((response) => {
                    const [error, data] = response;
                    if (error) {
                        this.setState({
                            errors: data
                        });
                    } else {
                        alert(id);
    
                        this.setState({
                            category: data,
                            errors: []
                        });
                    }
                });
        }
    }
Preetham
  • 87
  • 3
1

You can add more state to Categories to keep track of additional data about the modal.

*I have only included the highlights in the code here; lots was left out for brevity.

In Categories.jsx:

  constructor(props) {
    super(props) 
    this.state = {
      categories: [],
      isLoaded: false,
      error: null,
      isOpen: false,
      modalData: null,
    }
  }

  openModal = (modalData) => this.setState({ isOpen: true, modalData });
  closeModal = () => this.setState({ isOpen: false, modalData: null });

  //'create' dialog button
        <Button className="float-right" variant="primary" onClick={e => this.openModal({modalType: 'create'})}>
          Adicionar
        </Button>


    //here are the table rows:
   {categories.map(category => (
              <tr key={category.id}>
                <td>{category.id}</td>
                <td>{category.title}</td>
                <td>{category.slug}</td>
                <td>
                

                  <Button className="float-right mr-2" variant="primary" onClick={e => this.openModal({modalType: 'edit', categoryId: category.id})}>
                    Modal Edit
                  </Button>

                </td>
              </tr>
            ))}

      //the modal. pass modalData as a prop:
     <Modal show={this.state.isOpen} onHide={this.closeModal}>
          <Modal.Header closeButton>
            <Modal.Title>Adicionar / Editar</Modal.Title>
          </Modal.Header>
          <Modal.Body>
            <CategoryForm modalData={modalData} />
          </Modal.Body>
          <Modal.Footer>
            <Button variant="secondary" onClick={this.closeModal}>
              Close
            </Button>
          </Modal.Footer>
        </Modal>

In CategoryForm.jsx:

  //get id from props:
  getCategoryId(props) {
    return (props.modalData.modalType === 'edit') ? props.modalData.categoryId : false;

    //I don't know if this needs to be here:
    //try {
    //  return props.match.params.id
    //} catch (error) {
    //  return null
    //}
  }

You might have to refactor CategoryForm.jsx. For instance, the categoryId is now a prop, so it doesn't need to be duplicated in state.

Neal Burns
  • 839
  • 4
  • 5