React and Express

Hi All, I am having a difficult time with my react app updating items in the backend of my express API i made. Its an array of items on a json file and fetch them using axios, however, when I want to update an item I seem to be failing to get it to update.

import React, { Component } from 'react';
import axios from 'axios';
import { Input, FormGroup, Label, Modal, ModalHeader, ModalBody, ModalFooter, Table, Button } from 'reactstrap';

class Apps extends Component {
  state = {
    items: [],
    newItemModal: {
      title: '',
      description: '',
      url: ''
    },
    editItemData: {
        title: '',
        description: '',
        url: ''
    },
    newItemModal: false,
    editItemModal: false
  }
  componentWillMount() {
    this._refreshItems();
  }
  toggleNewItemModal() {
    this.setState({
      newItemModal: ! this.state.newItemModal
    });
  }
  toggleEditItemModal() {
    this.setState({
      editItemModal: ! this.state.editItemModal
    });
  }
  addItem() {
    axios.post("/api/add", this.state.newItemData).then((response) => {
      let { items } = this.state;

      items.push(response.data);

      this.setState({ items, newItemModal: false, newItemModal: {
        title: '',
        description: '',
        url: ''
      }});
    });
  }
  updateItem() {
    let { title, description, url } = this.state.editItemData;

    axios.put('/api/' + this.state.editItemData.id, {
      title, description, url
    }).then((_response) => {
      this._refreshItems();

      this.setState({
        editItemModal: false, editItemData: { title: '', url: '' }
      })
    });
  }
  editItem(title, description, url) {
    this.setState({
      editItemData: { title, description, url }, editItemModal: ! this.state.editItemModal
    });
  }
  deleteItem(id) {
    axios.delete("/api/:id").then((response) => {
      this._refreshItems();
    });
  }
  _refreshItems() {
    console.log("Mounted")
    axios.get("/api").then(data => this.setState(data.data));
  }

  render() {
    let items = this.state.items.length && this.state.items.map((item) => {
      return (
        <tr key={item.id}>
          <td>{item.title}</td>
          <td>{item.description}</td>
          <td>{item.url}</td>
          <td>
            <Button color="success" size="sm" className="mr-2" onClick={this.editItem.bind(this, item.title, item.url)}>Edit</Button>
            <Button color="danger" size="sm" onClick={this.deleteItem.bind(this, item.id)}>Delete</Button>
          </td>
        </tr>
      )
    });
    return (
      <div className="App container">

      <h1>Project Items App</h1>

      <Button className="my-3" color="primary" onClick={this.toggleNewItemModal.bind(this)}>Add Item</Button>

      <Modal isOpen={this.state.newItemModal} toggle={this.toggleNewItemModal.bind(this)}>
        <ModalHeader toggle={this.toggleNewItemModal.bind(this)}>Add a new Item</ModalHeader>
        <ModalBody>
          <FormGroup>
            <Label for="title">Title</Label>
            <Input id="title" value={this.state.newItemModal.title} onChange={(e) => {
              let { newItemModal } = this.state;

              newItemModal.title = e.target.value;

              this.setState({ newItemModal });
            }} />
          </FormGroup>

          <FormGroup>
          <Label for="description">description</Label>
          <Input id="description" value={this.state.newItemModal.description} onChange={(e) => {
              let { newItemModal } = e.target.value;

              newItemModal.description = e.target.value;

              this.setState({ newItemModal });
            }} />
          </FormGroup>

          <FormGroup>
            <Label for="url">url</Label>
            <Input id="url" value={this.state.newItemModal.url} onChange={(e) => {
              let { newItemModal } = this.state;

              newItemModal.url = e.target.value;

              this.setState({ newItemModal });
            }} />
          </FormGroup>

        </ModalBody>
        <ModalFooter>
          <Button color="primary" onClick={this.addItem.bind(this)}>Add Item</Button>{' '}
          <Button color="secondary" onClick={this.toggleNewItemModal.bind(this)}>Cancel</Button>
        </ModalFooter>
      </Modal>

      <Modal isOpen={this.state.editItemModal} toggle={this.toggleEditItemModal.bind(this)}>
        <ModalHeader toggle={this.toggleEditItemModal.bind(this)}>Edit a new Item</ModalHeader>
        <ModalBody>
          <FormGroup>
            <Label for="title">Title</Label>
            <Input id="title" value={this.state.editItemData.title} onChange={(e) => {
              let { editItemData } = this.state;

              editItemData.title = e.target.value;

              this.setState({ editItemData });
            }} />
          </FormGroup>

          <FormGroup>
          <Label for="description">description</Label>
          <Input id="description" value={this.state.editItemModal.description} onChange={(e) => {
              let { editItemModal } = this.state;

              editItemModal.description = e.target.value;

              this.setState({ editItemModal });
            }} />
          </FormGroup>

          <FormGroup>
            <Label for="url">url</Label>
            <Input id="url" value={this.state.editItemData.url} onChange={(e) => {
              let { editItemData } = this.state;

              editItemData.url = e.target.value;

              this.setState({ editItemData });
            }} />
          </FormGroup>

        </ModalBody>
        <ModalFooter>
          <Button color="primary" onClick={this.updateItem.bind(this)}>Update Item</Button>{' '}
          <Button color="secondary" onClick={this.toggleEditItemModal.bind(this)}>Cancel</Button>
        </ModalFooter>
      </Modal>


        <Table>
          <thead>
            <tr>
              <th>Title</th>
              <th>description</th>
              <th>URL</th>
              <th>Actions</th>
            </tr>
          </thead>

          <tbody>
            {items}
          </tbody>
        </Table>
      </div>
    );
  }
}

Here is my code to update, when I click on the Add Item modal, it opens up but when typing info into it I get an error: TypeError: Cannot create property ‘title’ on boolean ‘true’, so how or where did I miss something?

Hi Randell, thats the only error I get: TypeError: Cannot create property ‘title’ on boolean ‘true’

Here is the code from the backend:

const express = require('express');
const fileHandler = require('fs');
const app = express();
const router = express.Router();

// Middleware 
app.use(express.json());

let items = JSON.parse(fileHandler.readFileSync(`WebProject.json`));

// When the user navigates to the localhost once running the below will display
app.get('/api', (req, res) => {
    fileHandler.readFile(`WebProject.json`, 'utf8', (err, data) => {
        if (err) {
            res.send(`File not found. Please post to create the file.`);
        } else {
            // res.send(`Welcome to my first API\n\nSee below array list: \n\n${data}`);
            const parsedData = JSON.parse(data);
            res.json({ items: parsedData });
        }
    })
    // res.json({ items })
});

// Post request to post new Items to the json array
app.post('/api/add', (req, res) => {
    let newId = items[items.length - 1].id + 1;
    // Create a new object
    let newItem = Object.assign({
        id: newId,
        title: req.query.title,
        description: req.query.description,
        url: req.query.url
    });
    // Push the object into the array
    items.push(newItem);
    // The write the array to the json file
    fileHandler.writeFile(`WebProject.json`, JSON.stringify(items), (err) => {
        if (err) throw err;
        res.json(items)
        console.log(items);
    });
    // If there is no error resolve and send
    // res.send(`The item has been added to the json array list`);
});

// Delete request to delete an item by ID
app.delete('/api/:id', (req, res) => {
    // Get the ID the user inputs
    const id = req.params.id;
    // Filter the list
    const updatedItems = items.filter(item => item.id != id);
    // Find the selected item by ID
    let item = items.find((el) => el.id == id)
    console.log(item)
    // If the user enter an unknow ID tell the the ID does not exist
    if (item === undefined) {
        console.log({
            msg: `Item with the ID of ${id} is not found`
        });
        // If the ID matches, console log the updated list and tell them which ID item is about to be deleted
    } else {
        console.log(updatedItems + `is the updatedList`);
        // Do not include the item to be deleted
        items = updatedItems;
        console.log(`The item with the ID of ${id} is about to be deleted!!!`)
    }
    // Write the updated file to the json file
    fileHandler.writeFile(`WebProject.json`, JSON.stringify(items), (err) => {
        if (err) throw err;
    });
    res.send(`The chosen item is now gone!!!!`)
});

app.put("/api/:id", (req, res) => {
    // If the item being amended is not the title or description send the message
    if (!(req.query.title || !req.query.description)) {
        return res.send("Missing data...");
    }
    fileHandler.readFile(`WebProject.json`, 'utf8', (err, data) => {
        if (err) throw err;
        // Here the item is converted from a string to an object
        const parsedData = JSON.parse(data);
        // Here the itemIndex is being found using the findIndex finding the id of the item in the url in Postman
        const itemIndex = parsedData.findIndex(item => item.id === Number(req.params.id));
        console.log(itemIndex)
        // If the title is being amended change it else if the description is being changed amend the description
        // Only one of the two can be amended
        if (req.query.title) {
            parsedData[itemIndex].title = req.query.title;
        } else if (req.query.description) {
            parsedData[itemIndex].description = req.query.description;
        }
        // Write the data to the json file
        fileHandler.writeFile(`WebProject.json`, JSON.stringify(parsedData), err => {
            if (err) throw err;
            res.json(items);
        });
    });
});

// If the user navigates to a url not found, display the error
app.get("*", (req, res, next) => {
    let err = new Error(
        `Sorry! Can’t find that resource. Please check your URL\n`
    );
    err.statusCode = 404;
    next(err);
});

// If the user navigates to a url not found, display the error
app.get('*', (req, res, next) => {
    let err = new Error(`Sorry! Can’t find that resource. Please check your URL\n`);
    err.statusCode = 404;
    next(err);
});

// Specify the desired port ot the port assigned
const PORT = process.env.PORT || 3001;
//Listen on port set by either the device or port 8080 on localhost
app.listen(PORT, () => {
    console.log(`App is running on port ${PORT}`)
});

module.exports = app;

So essentially what I am trying to do is use the fron-end to update items in the json file via the back-end. Though, in the modal that opens, when I click on the title, description or url, it crashes with the error : TypeError: Cannot create property ‘title’ on boolean ‘true’

Also getting this error

TypeError: this.state.items.map is not a function

I was following along with a video and this is done there, also as per my understanding is, is that the newItemModal creates an array of the data being added. Will false break it to prevent the input?

Ah okay, so if I may, how would I rectify this?

Just to add, this is the original code I started with and got stuck so I looked up a video, not sure if I should stick to this code or the one from the video

import React, { Component } from 'react';
import './App.css';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';

class App extends Component {

  state = { items: [] }

  componentDidMount() {
    console.log("Mounted")
    axios.get("/api").then(data => this.setState(data.data));
  }

  handleChange = (event) => {
    this.setState({ [event.target.name]: event.target.value });
    console.log(event.target.name + ":", event.target.value)
  }

  handleSubmit() {
    axios.post("api/add").then(res => this.setState(res.response));
  }

  render() {
    return (
      <div className="App">
        <header className="App-header">
          <h1 className="h1Heading">Welcome to my Projects Page</h1>
          <br />
          <h2 className="h2Info">Below is a list of projects, which can be amended or new ones can be added as well</h2>
          <br />
          <h3 className="projectsList">Below is a list of all projects</h3>
          <ul>
            {this.state.items.length && this.state.items.map(item => //console.log(item) ||
              <li
                key={item.id}> <br />
                <strong><span role="img" aria-label="PC">💻</span> {item.title}</strong> <br />
                <span role="img" aria-label="Document">📑</span> {item.description} <br />
                <span role="img" aria-label="Link">🔗</span> {item.url}
              </li>
            )}
          </ul>
          <br />
          <br />
          <Form id="formName" onSubmit={this.handleSubmit}>
            <Form.Group>
              <Form.Label>Enter Project Info:</Form.Label>
              <br />
              <Form.Control type="text" placeholder="Enter Title" id="title" name="title" onChange={this.handleChange} />
              <br />
              <Form.Control type="text" placeholder="Enter Description" id="descriotion" name="description" onChange={this.handleChange} />
              <br />
              <Form.Control type="text" placeholder="Enter URL" id="url" name="url" onChange={this.handleChange} />
            </Form.Group>
            <Button variant="primary" type="submit">Submit</Button>
          </Form>
          <br />
          <br />
          <br />
        </header>
      </div>
    )
  }

}

export default App;

Yeah, watched it again, seems to be with a few errors and amendments due to the way my API is created, see below the updated working code

import React, { Component } from 'react';
import axios from 'axios';
import { Input, FormGroup, Label, Modal, ModalHeader, ModalBody, ModalFooter, Table, Button } from 'reactstrap';

class App extends Component {
  state = {
    items: [],
    newItemData: {
      title: '',
      description: '',
      url: ''
    },
    editItemData: {
      id: "",
      title: "",
      description: "",
      url: ""
    }
  }

  componentDidMount() {
    this._refreshItems();
  }

  toggleNewItemModal() {
    this.setState({
      newItemModal: !this.state.newItemModal
    });
  }

  toggleEditItemModal() {
    this.setState({
      editItemModal: !this.state.editItemModal
    });
  }

  addItem() {
    axios.post("/api/add", this.state.newItemData).then((response) => {
      let { items } = this.state;
      items.push(response.data);
      this.setState(
        {
          items,
          newItemModal: false,
          newItemData: {
            title: '',
            description: '',
            url: ''
          }
        }, this._refreshItems()
      );
    });
  }

  updateItem() {
    let { id, title, description, url } = this.state.editItemData;
    console.log("id", id);
    axios.put("/api/" + id, {
      title,
      description,
      url
    })
      .then(response => {
        this._refreshItems();
        this.setState({
          editItemModal: false,
          editItemData: { id: "", title: "", description: "", url: "" }
        });
      });
  }

  editItem(id, title, description, url) {
    this.setState({
      editItemData: { id, title, description, url },
      editItemModal: !this.state.editItemModal
    });
  }

  deleteItem(id) {
    axios.delete(`/api/${id}`).then((response) => {
      this._refreshItems();
    });
  }

  _refreshItems() {
    console.log("Mounted")
    axios.get("/api").then(data => this.setState(data.data));
  }

  render() {
    let items = this.state.items.length && this.state.items.map((item) => {
      return (
        <tr key={item.id}>
          <td>{item.title}</td>
          <td>{item.description}</td>
          <td>{item.url}</td>
          <td>
            <Button
              color="success"
              size="sm"
              className="mr-2"
              onClick={this.editItem.bind(
                this,
                item.id,
                item.title,
                item.description,
                item.url
              )}>
              Edit
            </Button>

            <Button
              color="danger"
              size="sm"
              onClick={this.deleteItem.bind(
                this, 
                item.id
                )}>
              Delete
            </Button>

          </td>
        </tr>
      )
    });
    return (
      <div className="App container">

        <h1>Project Items App</h1>

        <Button className="my-3" color="primary" onClick={this.toggleNewItemModal.bind(this)}>Add Item</Button>

        <Modal isOpen={this.state.newItemModal} toggle={this.toggleNewItemModal.bind(this)}>
          <ModalHeader toggle={this.toggleNewItemModal.bind(this)}>Add a new Item</ModalHeader>
          <ModalBody>

            <FormGroup>
              <Label for="title">Title</Label>
              <Input id="title" value={this.state.newItemData.title} onChange={(e) => {
                let { newItemData } = this.state;
                console.log(`This here: ${this.state.newItemData.title}`)
                newItemData.title = e.target.value;
                this.setState({ newItemData });
              }} />
            </FormGroup>

            <FormGroup>
              <Label for="description">Description</Label>
              <Input id="description" value={this.state.newItemData.description} onChange={(e) => {
                let { newItemData } = this.state;
                newItemData.description = e.target.value;
                this.setState({ newItemData });
              }} />
            </FormGroup>

            <FormGroup>
              <Label for="url">url</Label>
              <Input id="url" value={this.state.newItemData.url} onChange={(e) => {
                let { newItemData } = this.state;
                newItemData.url = e.target.value;
                this.setState({ newItemData });
              }} />
            </FormGroup>

          </ModalBody>
          <ModalFooter>
            <Button color="primary" onClick={this.addItem.bind(this)}>Add Item</Button>{' '}
            <Button color="secondary" onClick={this.toggleNewItemModal.bind(this)}>Cancel</Button>
          </ModalFooter>
        </Modal>

        <Modal isOpen={this.state.editItemModal} toggle={this.toggleEditItemModal.bind(this)}>
          <ModalHeader toggle={this.toggleEditItemModal.bind(this)}>Edit a new item</ModalHeader>
          <ModalBody>

            <FormGroup>
              <Label for="title">Title</Label>
              <Input id="title" value={this.state.editItemData.title} onChange={(e) => {
                let { editItemData } = this.state;
                editItemData.title = e.target.value;
                this.setState({ editItemData });
              }} />
            </FormGroup>

            <FormGroup>
              <Label for="description">Description</Label>
              <Input id="description" value={this.state.editItemData.description} onChange={(e) => {
                let { editItemData } = this.state;
                editItemData.description = e.target.value;
                this.setState({ editItemData });
              }} />
            </FormGroup>

          </ModalBody>
          <ModalFooter>
            <Button color="primary" onClick={this.updateItem.bind(this)}>Update Item</Button>{' '}
            <Button color="secondary" onClick={this.toggleEditItemModal.bind(this)}>Cancel</Button>
          </ModalFooter>
        </Modal>


        <Table>
          <thead>
            <tr>
              <th>Title</th>
              <th>Description</th>
              <th>url</th>
              <th>Actions</th>
            </tr>
          </thead>

          <tbody>
            {items}
          </tbody>
        </Table>
      </div>
    );
  }
}

export default App;

and the changes to the API is here

const express = require('express');
const fileHandler = require('fs');
const app = express();
const router = express.Router();

// Middleware 
app.use(express.json());

let items = JSON.parse(fileHandler.readFileSync(`WebProject.json`));

// When the user navigates to the localhost once running the below will display
app.get('/api', (req, res) => {
    fileHandler.readFile(`WebProject.json`, 'utf8', (err, data) => {
        if (err) {
            res.send(`File not found. Please post to create the file.`);
        } else {
            // res.send(`Welcome to my first API\n\nSee below array list: \n\n${data}`);
            const parsedData = JSON.parse(data);
            res.json({ items: parsedData });
        }
    })
    // res.json({ items })
});

// Post request to post new Items to the json array
app.post('/api/add', (req, res) => {
    let newId = items[items.length - 1].id + 1;
    // Create a new object
    let newItem = Object.assign({
        id: newId,
        title: req.body.title,
        description: req.body.description,
        url: req.body.url
    });
    // Push the object into the array
    items.push(newItem);
    // The write the array to the json file
    fileHandler.writeFile(`WebProject.json`, JSON.stringify(items), (err) => {
        if (err) throw err;
        res.json(items)
        console.log(items);
    });
    // If there is no error resolve and send
    // res.send(`The item has been added to the json array list`);
});

// Delete request to delete an item by ID
app.delete('/api/:id', (req, res) => {
    // Get the ID the user inputs
    const id = req.params.id;
    // Filter the list
    const updatedItems = items.filter(item => item.id != id);
    // Find the selected item by ID
    let item = items.find((el) => el.id == id)
    console.log(item)
    // If the user enter an unknow ID tell the the ID does not exist
    if (item === undefined) {
        console.log({
            msg: `Item with the ID of ${id} is not found`
        });
        // If the ID matches, console log the updated list and tell them which ID item is about to be deleted
    } else {
        console.log(updatedItems + `is the updatedList`);
        // Do not include the item to be deleted
        items = updatedItems;
        console.log(`The item with the ID of ${id} is about to be deleted!!!`)
    }
    // Write the updated file to the json file
    fileHandler.writeFile(`WebProject.json`, JSON.stringify(items), (err) => {
        if (err) throw err;
    });
    res.send(`The chosen item is now gone!!!!`)
});

app.put("/api/:id", (req, res) => {
    // If the item being amended is not the title or description send the message
    console.log("id", req.params.id);
  
    if (!(req.body.title || !req.body.description)) {
      return res.send("Missing data...");
    }
    fileHandler.readFile(`WebProject.json`, "utf8", (err, data) => {
      if (err) throw err;
      // Here the item is converted from a string to an object
  
      const parsedData = JSON.parse(data);
      // Here the itemIndex is being found using the findIndex finding the id of the item in the url in Postman
      const itemIndex = parsedData.findIndex(
        item => item.id === Number(req.params.id));
  
      // If the title is being amended change it else if the description is being changed amend the description
      // Only one of the two can be amended
      if (req.body.title) {
        parsedData[itemIndex].title = req.body.title;
      } else if (req.body.description) {
        parsedData[itemIndex].description = req.body.description;
      }
      // Write the data to the json file
      fileHandler.writeFile(`WebProject.json`,JSON.stringify(parsedData),err => {
          if (err) throw err;
          res.json(items);
        }
      );
    });
  });

// If the user navigates to a url not found, display the error
app.get("*", (req, res, next) => {
    let err = new Error(
        `Sorry! Can’t find that resource. Please check your URL\n`
    );
    err.statusCode = 404;
    next(err);
});

// If the user navigates to a url not found, display the error
app.get('*', (req, res, next) => {
    let err = new Error(`Sorry! Can’t find that resource. Please check your URL\n`);
    err.statusCode = 404;
    next(err);
});

// Specify the desired port ot the port assigned
const PORT = process.env.PORT || 3001;
//Listen on port set by either the device or port 8080 on localhost
app.listen(PORT, () => {
    console.log(`App is running on port ${PORT}`)
});

module.exports = app;

I did try without the _refreshItems but for some reason after adding a new item it doesn’t refresh or show the item untill the page is refreshed, don’t know why yet though, but will figure this out :slight_smile:

:thinking: I need to figure out how to add that. In the back-end yes?

True yes, had a look now. Its my front-end that needs to be set