I have develope application in reactjs and nodejs with postgresql database, when i call api using axios from AxiosTable.js file data from table is fetched successfully and displayed in console but when i try to display in table it doesnot work, so tell me what i do wrong here?
server.js
var restify=require('restify')
const {empdetails} = require('./Function');
var server=restify.createServer() //server created
server.get('/empdetails',empdetails)
server.listen(8080, function(){
console.log("server started...")
})
AxiosTable.js
import React, { Component } from 'react'
import axios from 'axios'
export class AxiosTable extends Component {
state={
persons:[]
}
componentDidMount(){
axios.get('http://localhost:8080/empdetails')
.then(res => {
const persons = res.data;
this.setState({ persons });
})
}
render() {
return (
<div className="App">
<div className="left">
<table className="table table-hover table-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
{ this.state.persons.map(person =>
<tr>
<td>{person.id}</td>
<td>{person.name}</td>
<td>{person.email}</td>
</tr>
)}
</table>
</div> </div>
)
}
}
export default AxiosTable
Function.js //backend code
var Sequelize=require('sequelize')
const connection = new Sequelize('mydb', 'postgres', 'password', {
host: 'localhost',
dialect: 'postgres'
});
var Demo=connection.define('demo',{
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
}
})
module.exports ={
// employee details fetched
empdetails: function empdetails(req,res,next){
res.send('employee details ')
connection.sync().then(function(){
Demo.findAll().then(function(demos){
console.log(demos)
})
})
},
};
Try this
componentDidMount(){
axios.get('http://localhost:8080/empdetails')
.then(res => {
const persons = res.data;
this.setState(prevState => { persons: [...prevState.persons, persons] });
})
}
Your backend is not actually returning any data, it looks like.
Try something like
function empdetails(req, res, next) {
connection.sync().then(() => {
Demo.findAll().then((demos) => {
console.log(demos);
res.send(demos); // <-- This is the important bit! :)
});
});
}
instead.
Related
I'm working on a web app to track fruits vendor stock using React.js, MongoDB, Node.js, and Express. I called my database endpoints to render the data in the table. Now I try to increment and decrement the stock amount using a button, but when I try to set the new state, it doesn't work. I try to change the state by clicking, then update the new state in the database. Any advice?
FruitTable component:
import Reac, { Component } from 'react';
import * as ReactBootstrap from 'react-bootstrap';
import axios from 'axios';
import Button from 'react-bootstrap/Button';
class FruitTable extends Component {
constructor(props) {
super(props)
this.state = {
fruits: []
}
this.handleClick = this.handleClick.bind(this)
}
componentDidMount() {
axios.get('http://localhost:5000/fruits/')
.then(res => {
this.setState({
fruits: res.data
});
})
.catch((error) => {
console.log(error)
})
}
handleClick = () => {
const fruits = [...this.state.fruits]
this.setState({fruits: this.state.fruits[1] +1})
}
render() {
return(
<div>
<h1>Fruit Vendor Stock</h1>
<ReactBootstrap.Table striped bordered hover size="sm">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Stock</th>
<th>Price</th>
<th>Add/Reomve</th>
</tr>
</thead>
<tbody>
{
this.state.fruits.map((fruit, index) => (
<tr>
<th scope="row">{index + 1}</th>
<td>{fruit.name}</td>
<td>{fruit.stock}</td>
<td>{fruit.price}</td>
<td>
<div className="mb2">
<Button className="btn btn-primary btn-sm"
onClick={this.handleClick}
>Add</Button>{' '}
<Button className="btn btn-danger btn-sm"
>Remove</Button>
</div>
</td>
</tr>
))
}
</tbody>
</ReactBootstrap.Table>
</div>
)
};
}
export default FruitTable;
FruitsData.model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const fruits = new Schema({
name: { type: String, required: true },
stock: { type: Number, required: true },
price: { type: Number, required: true },
}, {
});
const Fruits = mongoose.model('Fruits', fruits);
module.exports = Fruits;
-Routes:
const router = require('express').Router();
let Fruit = require('../models/fruit_data');
router.route('/').get((req, res) => {
Fruit.find()
.then(fruits => res.json(fruits))
.catch(err => res.status(400).json('Error: ' + err));
});
router.route('/add').post((req, res) => {
const name = req.body.name;
const stock = Number(req.body.stock);
const price = Number(req.body.price);
const newFruit = new Fruit({
name,
stock,
price,
});
newFruit.save()
.then(() => res.json('Fruit has been added!'))
.catch(err => res.status(400).json('Error: ' + err));
});
router.route('/:id').get((req, res) => {
Fruit.findById(req.params.id)
.then(Fruit => res.json(Fruit))
.catch(err => res.status(400).json('Error: ' + err));
});
router.route('/:id').delete((req, res) => {
Fruit.findByIdAndDelete(req.params.id)
.then(() => res.json('Fruit has deleted.'))
.catch(err => res.status(400).json("Error: " + err));
});
router.route('/update/:id').put((req, res, next) => {
Fruit.findByIdAndUpdate(req.params.id, {
$set: req.body
}, (error, data) => {
if(error) {
return next(error);
console.log(error)
} else {
res.json(data)
console.log('Stock has been updated')
}
})
})
module.exports = router;
-Server.js:
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
const uri = process.env.ATLAS_URI;
mongoose.connect(uri, { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once('open', () => {
console.log("MongoDb database connection established successfully!!")
})
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
const fruitsRouter = require('./routes/fruit');
app.use('/fruits', fruitsRouter);
app.listen(port, () => {
console.log(`Server is running on port: ${port}`);
})
-App.js:
import "./App.css";
import React, { Component, useState, useEffect } from "react";
import FruitTable from "./Components/fruitTable";
import "bootstrap/dist/css/bootstrap.min.css";
import { sendEmail } from "./service/emailService";
import axios from 'axios';
function App() {
return (
<div className="App">
<FruitTable />
</div>
);
}
export default App;
The state update in handleClick isn't correct, it is taking the 1th element value, adding 1 to it, and setting this as the this.state.fruits state value. This breaks the state invariant of fruits being an array. This is likely the "crash" you are seeing as a non-array value is attempted to be mapped.
handleClick = () => {
const fruits = [...this.state.fruits]
this.setState({
fruits: this.state.fruits[1] +1 // <-- mutates state invariant!!
})
}
The code also isn't passing a reference to which index or fruit it is trying to update.
According to your schema the fruit items have 3 properties, name, stock, and price. For this solution I'll assume that name is sufficiently unique for identification purposes and that you are incrementing/decrementing the stock quantity.
handleClick = (name, addQuantity = 1) => () => {
this.setState(prevState => ({
fruits: prevState.fruits.map(fruit =>
fruit.name === name
? { ...fruit, stock: fruit.stock + addQuantity}
: fruit
)
}));
}
Now you need to pass the currently mapped fruit item's name property to the handler when mapping.
{this.state.fruits.map((fruit, index) => (
<tr key={fruit.name}>
<th scope="row">{index + 1}</th>
<td>{fruit.name}</td>
<td>{fruit.stock}</td>
<td>{fruit.price}</td>
<td>
<div className="mb2">
<Button
className="btn btn-primary btn-sm"
onClick={this.handleClick(item.name, 1)} // <-- add one
>
Add
</Button>{' '}
<Button
className="btn btn-danger btn-sm"
onClick={this.handleClick(item.name, -1)} // remove one
>
Remove
</Button>
</div>
</td>
</tr>
))}
I am trying to delete a specific to-do item using the to-do's id. I am, however, experiencing the following error:
DELETE http://localhost:3000/users/todo/delete/undefined 404 (Not Found)
Please see below:
My code is as follows:
- GetToDoList:
import React, { useState, useEffect } from "react";
import axios from "axios";
import moment from "moment";
import DeleteToDo from "./DeleteToDo";
const TodoList = ({ data }) => {
const { Date, Todo, Due } = data;
return (
<tr>
<td>{moment(Date).format("L")}</td>
<td className="todotext">{Todo}</td>
<td>{moment(Due).format("L")}</td>
<td className="tdactions">
<DeleteToDo />
</td>
</tr>
);
};
const GetToDoList = () => {
const [todos, setTodos] = useState([]);
useEffect(() => {
axios({
url: "/todos/gettodo",
method: "get",
headers: {
"Content-type": "application/json",
},
})
.then((res) => {
const data = res.data;
console.log("data:", data);
setTodos(data.todos);
})
.catch((err) => console.log(err));
}, []);
return (
<div>
<header>
<h1>To Do List</h1>
</header>
<table>
<thead>
<tr>
<th className="thdate">CREATED:</th>
<th id="thtodo">TO-DO:</th>
<th className="thdate">DUE:</th>
</tr>
</thead>
<tbody>
{todos && todos.length >= 0
? todos.map((todo, _id) => <TodoList data={todo} key={_id} />)
: null}
</tbody>
</table>
</div>
);
};
export default GetToDoList;
- DeleteToDo:
import React, { useState } from "react";
import axios from "axios";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faTrashAlt } from "#fortawesome/free-solid-svg-icons";
import Swal from "sweetalert2";
const DeleteToDo = ({ _id }) => {
const [todos, setTodos] = useState([]);
const remove = (e, _id) => {
e.preventDefault();
axios
.delete(`todo/delete/${_id}`) //Have to send the JWT back to the Server, send via headers
.then((response) => {
const del = todos.filter((todo) => _id !== todo._id);
setTodos(del);
console.log("response", response);
Swal.fire({
icon: "success",
confirmButtonColor: "#000000",
timer: 3000,
width: 400,
title: "SUCCESS!",
text: response.data.message,
}).then(function () {
window.location.reload();
});
})
.catch((error) => {
Swal.fire({
icon: "error",
confirmButtonColor: "#ff0000",
width: 400,
title: "ERROR!",
text: error.response.data.message,
}).then(function () {
window.location.reload();
});
});
};
return (
<div>
<td className="tdactions">
<FontAwesomeIcon
icon={faTrashAlt}
onClick={(e) => remove(e, _id)}
id="removebutton"
title="Remove"
/>
</td>
</div>
);
};
export default DeleteToDo;
- TodoControllers:
const Todo = require("../models/todoModels.js");
const mongoose = require("mongoose");
exports.createController = (req, res) => {
let todo = new Todo({
Todo: req.body.Todo,
Date: req.body.Date,
Due: req.body.Due,
});
todo
.save()
.then((todos) => {
return res.json({ message: "Todo created successfully.", todos });
})
.catch((err) => {
return res.status(400).json({ message: "Error creating the todo.", err });
});
};
exports.getAllTodosController = (req, res, next) => {
Todo.find({})
.then((todos) => {
return res.json({ secret: "resource", todos });
})
.catch((err) => {
return res
.status(400)
.json({ message: "Error getting the todos' information.", err });
});
};
exports.removeOneController = (req, res) => {
Todo.findByIdAndRemove(req.params.id)
.then((todos) => {
return res.json({ message: "Todo deleted successfully.", todos });
})
.catch((err) => {
return res
.status(400)
.json({ message: "Error deleting the todo item.", err });
});
};
I am using the id as the unique key prop of the todos in order to delete that particular list item.
Please may someone assist?
A lot of thanks to Shyam, as he has helped me tremendously sorting this out.
As he has mentioned, I wasn't passing the _id to the "DeleteToDo" component. I needed to add it as follows:
<DeleteToDo _id={_id}/>
It was also drawing from localhost:3000/users/todo/delete/612dc2e3e0338ed3fcd7004f instead of localhost:3000/todo/delete/612dc2e3e0338ed3fcd7004f.
After changes to the DeleteToDo component and routing, all seems to be in order.
I'm trying to build a web app that uses Spotify API now. I want it to send a search keyword that an user submits to the server and send back its search result to the front end. The problem is I get a 404 status code for the fetch call. The POST request works fine.
Main.js
import React, { Component } from "react";
import SingerCard from "./SingerCard";
import axios from "axios";
export class Main extends Component {
constructor(props) {
super(props);
this.state = {
keyword: "",
artists: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({ keyword: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
axios
.post(
"http://localhost:4000/search_result",
{
keyword: this.state.keyword,
},
{
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
}
)
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.log(err);
});
}
componentDidMount() {
fetch("http://localhost:4000/api")
.then((res) => res.json)
.then((artists) => {
this.setState({ artists });
});
}
render() {
return (
<div className="main">
<form onSubmit={this.handleSubmit}>
<label htmlFor="search">Search an artist: </label>
<span>
<input
type="search"
value={this.state.keyword}
onChange={this.handleChange}
name="keyword"
/>
<button type="submit" value="Submit">
Search
</button>
</span>
</form>
<br />
<div className="container">
{this.state.artists.map((elem) => (
<SingerCard
images={elem.images}
name={elem.name}
artists={this.state.artists}
/>
))}
{console.log(this.state.artists)}
</div>
<br />
</div>
);
}
}
export default Main;
server.js
const express = require("express");
const SpotifyWebApi = require("spotify-web-api-node");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
const port = 4000 || process.env.PORT;
require("dotenv").config();
app.use(express.json());
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
// Create the api object with the credentials
var spotifyApi = new SpotifyWebApi({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
});
// Retrieve an access token.
spotifyApi.clientCredentialsGrant().then(
function (data) {
console.log("The access token expires in " + data.body["expires_in"]);
console.log("The access token is " + data.body["access_token"]);
// Save the access token so that it's used in future calls
spotifyApi.setAccessToken(data.body["access_token"]);
},
function (err) {
console.log("Something went wrong when retrieving an access token", err);
}
);
app.post("/search_result", (req, res) => {
console.log(req.body.keyword);
spotifyApi.searchArtists(req.body.keyword).then(function (data) {
var search_res = data.body.artists.items;
res.json(search_res);
app.get("http://localhost:/api", (req, res) => {
res.json(search_res);
res.end();
});
res.end();
}),
function (err) {
console.log(err);
};
});
app.listen(port, () => console.log(`It's running on port ${port}`));
I think the app.get() in the app.post() causes the error but I can't figure out another way to send the search result back.
You're getting a 404 because the get method is not correctly defined.
Update your server code to define the get method to just keep the pathname, like this:
app.get("/api", (req, res) => {
// ...
}
Currently, you are defining this route inside the app.post. The get route definition should be outside of the post route.
Use Axios.get
import React, { Component } from "react";
// import SingerCard from "./SingerCard";
import axios from "axios";
export class Main extends Component {
constructor(props) {
super(props);
this.state = {
keyword: "",
artists: []
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({ keyword: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
const headers = {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
};
axios.post(
"https://jsonplaceholder.typicode.com/users",
{ keyword: this.state.keyword },
{ headers: headers }
)
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
});
}
componentDidMount() {
axios.get("https://jsonplaceholder.typicode.com/users").then(res => {
this.setState({
artists: res.data
});
});
}
render() {
return (
<div className="main">
<form onSubmit={this.handleSubmit}>
<label htmlFor="search">Search an artist: </label>
<span>
<input
type="search"
value={this.state.keyword}
onChange={this.handleChange}
name="keyword"
/>
<button type="submit" value="Submit">
Search
</button>
</span>
</form>
<br />
<div className="container">
{this.state.artists.map(elem => (
<div key={elem.id}>
<ul>
<li>{elem.name}</li>
</ul>
</div>
))}
</div>
</div>
);
}
}
export default Main;
I'm building a TODO list and one of the things that it needs to do is delete.
Here is my server.js code
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
const cpdRoutes = express.Router();
const PORT = 4000;
let Cpd = require("./cpd.model");
app.use(cors());
app.use(bodyParser.json());
//connects my backend to my mongo database
mongoose.connect('mongodb://127.0.0.1:27017/cpds', { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once('open', function() {
console.log("MongoDB database connection established successfully");
})
cpdRoutes.route('/').get(function(req, res) {
Cpd.find(function(err, cpds) {
if (err) {
console.log(err);
}
else {
res.json(cpds);
}
});
});
//finds the data by id
cpdRoutes.route('/:id').get(function(req, res) {
let id = req.params.id;
Cpd.findById(id, function(err, cpd) {
res.json(cpd);
});
});
//creating data
cpdRoutes.route('/add').post(function(req, res) {
let cpd = new Cpd(req.body);
cpd.save()
.then(cpd => {
res.status(200).json({'cpd': 'New data added successfully'});
})
.catch(err => {
res.status(400).send('Adding new data failed');
});
});
//update data
cpdRoutes.route('/update/:id').post(function(req, res) {
Cpd.findById(req.params.id, function(err, cpd) {
if (!cpd)
res.status(404).send("data is not found");
else
cpd.cpd_date = req.body.cpd_date;
cpd.cpd_activity = req.body.cpd_activity;
cpd.cpd_hours = req.body.cpd_hours;
cpd.cpd_learningStatement = req.body.cpd_learningStatement;
cpd.save().then(cpd => {
res.json('Data updated!');
})
.catch(err => {
res.status(400).send("Update not possible");
});
});
});
// cpdRoutes.route('/delete/:id').post(function(req, res) {
// Cpd.findById(req.params.id, function(err, cpd) {
// if (!cpd)
// res.status(404).send("data is not found");
// else
// cpd.cpd_date = req.body.cpd_date;
// cpd.cpd_activity = req.body.cpd_activity;
// cpd.cpd_hours = req.body.cpd_hours;
// cpd.cpd_learningStatement = req.body.cpd_learningStatement;
// cpd.save().then(cpd => {
// res.json('Data updated!');
// })
// .catch(err => {
// res.status(400).send("Update not possible");
// });
// });
// });
cpdRoutes.route.get('/delete', function(req, res){
var id = req.query.id;
Cpd.find({_id: id}).remove().exec(function(err, expense) {
if(err)
res.send(err)
res.send('Data successfully deleted!');
})
});
app.use('/cpds', cpdRoutes);
app.listen(PORT, function() {
console.log("Server is running on Port: " + PORT);
});
My delete component:
import React from 'react';
import axios from 'axios';
import { Button } from 'react-bootstrap';
import { Link } from 'react-router-dom';
class DeleteCpd extends React.Component {
constructor(){
super();
this.state={id:''};
this.onClick = this.onClick.bind(this);
this.delete = this.delete.bind(this);
}
// componentDidMount() {
// this.setState({
// id: this.props.cpds.id
// })
// }
componentDidMount() {
axios.get('http://localhost:4000/cpds/'+this.props.match.params.id)
.then(response => {
this.setState({
cpd_date: response.data.cpd_date,
cpd_activity: response.data.cpd_activity,
cpd_hours: response.data.cpd_hours,
cpd_learningStatement: response.data.cpd_learningStatement
})
})
.catch(function (error) {
console.log(error);
})
}
onClick(e){
this.delete(this);
}
delete(e){
axios.get('http://localhost:4000/cpds/'+this.props.match.params.id)
.then(function(response) {
});
}
render(){
return (
<Button onClick={this.onClick}>
<Link to={{pathname: '/', search: '' }} style={{ textDecoration: 'none' }}>
<span className="glyphicon glyphicon-remove"></span>
</Link>
</Button>
)
}
}
export default DeleteCpd;
and my App.js:
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import CreateCpd from "./components/create-cpd.component";
import EditCpd from "./components/edit-cpd.component";
import CpdsList from "./components/cpds-list.component";
import DeleteCpd from "./components/cpds-delete.component";
class App extends Component {
render() {
return (
<Router>
<div className="container">
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<Link to="/" className="navbar-brand">MERN-Stack Cpd tracker App</Link>
<div className="collpase navbar-collapse">
<ul className="navbar-nav mr-auto">
<li className="navbar-item">
<Link to="/" className="nav-link">Data List</Link>
</li>
<li className="navbar-item">
<Link to="/create" className="nav-link">Create Cpd data</Link>
</li>
</ul>
</div>
</nav>
<br/>
<Route path="/" exact component={CpdsList} />
<Route path="/edit/:id" component={EditCpd} />
<Route path="/delete/:id" component={DeleteCpd} />
<Route path="/create" component={CreateCpd} />
</div>
</Router>
);
}
}
export default App;
This is the error my getting:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in CpdList (created by Context.Consumer)
What I'm trying to do is delete via id. What am I doing wrong?
This is my CPDList:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
// import { CSVLink } from "react-csv";
// import DeleteCpd from './cpd_delete.component';
const Cpd = props => (
<tr>
<td>{props.cpd.cpd_date}</td>
<td>{props.cpd.cpd_activity}</td>
<td>{props.cpd.cpd_hours}</td>
<td>{props.cpd.cpd_learningStatement}</td>
<td>{props.cpd.cpd_evidence}</td>
<td>
<Link to={"/edit/"+props.cpd._id}>Edit</Link>
</td>
<td>
<Link to={"/delete/"+props.cpd._id}>Delete(not working yet)</Link>
</td>
</tr>
)
export default class CpdList extends Component {
constructor(props) {
super(props);
this.state = {
cpds: [],
// csvData:[
// {
// "date": ""
// },
// {
// "activity": ""
// },
// {
// "hours": ""
// },
// {
// "learningStatement": ""
// },
// {
// "evidence": ""
// }
// ]
};
};
// exportCsv()
// {
// var csvRow=[];
// }
componentDidMount() {
axios.get('http://localhost:4000/cpds/')
.then(response => {
this.setState({ cpds: response.data });
})
.catch(function (error){
console.log(error);
});
};
componentDidUpdate() {
axios.get('http://localhost:4000/cpds/')
.then(response => {
this.setState({ cpds: response.data });
})
.catch(function (error){
console.log(error);
});
}
cpdList() {
return this.state.cpds.map(function(currentCpd, i){
return <Cpd cpd={currentCpd} key={i} />;
});
}
render() {
return(
<div>
<h3>Cpd Data List</h3>
<table className="table table-striped" style={{ marginTop: 20 }} >
<thead>
<tr>
<th>Date</th>
<th>Activity</th>
<th>Hours</th>
<th>Learning Statement</th>
<th>Evidence</th>
</tr>
</thead>
<tbody>
{ this.cpdList() }
</tbody>
</table>
{/* <CSVLink data={csvData}
filename={"db.csv"}
color="primary"
style={{float: "left", marginRight: "10px"}}
className="btn btn-primary"
>Download .CSV
</CSVLink> */}
</div>
)
}
};
please ignore the commented out code still working on that.
Here i am getting the list of google drive files in '/dashboard/gdrive'. There is one more event of uploading a new file. On uploading the redux state shows that the new file is stored in state but whil re-rendering i am not able to access the store states.
Here is the codeblock
import React, { Component } from 'react';
import PropTypes from "prop-types";
import {connect} from "react-redux";
import {logoutUser} from "./../../actions/authActions.js";
import {syncCloud} from "./../../actions/cloudActions.js";
import {uploadFile} from "./../../actions/cloudActions.js";
class Gdrive extends Component {
constructor(){
super();
this.state={
file: null
}
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.handleCancelEvent = this.handleCancelEvent.bind(this);
}
onChange(e){
this.setState({
file: e.target.files[0]
});
}
onSubmit(e){
e.preventDefault();
const data = new FormData();
data.append('filename', this.state.file);
this.props.uploadFile(data);
}
handleCancelEvent(){
this.setState({
file: null
})
}
render() {
return (
<div>
<table>
<thead>
<tr><th>fileid</th><th>filename</th></tr>
</thead>
<tbody>
{this.props.cloud.files.data.map(file =>(
<tr>
<td>{file.id}</td>
<td>{file.name}</td>
</tr>
))}
</tbody>
</table>
<fieldset>
<form noValidate onSubmit={this.onSubmit}>
<div>
<label>filename</label>
<input type="file" onChange={this.onChange} name= "filename"></input>
</div>
<div>
<button type="submit">Upload</button>
<button onClick={this.handleCancelEvent}>Cancel</button>
</div>
</form>
</fieldset>
</div>
)
}
}
Gdrive.propTypes = {
logoutUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth,
cloud: state.cloud
});
export default connect(
mapStateToProps,
{ logoutUser, syncCloud, uploadFile }
)(Gdrive);
and here is uploadFile action
export const uploadFile = (file) => dispatch => {
axios.post('/gdrive/upload', file)
.then(rslt => {
dispatch({
type: UPLOAD_FILE,
payload: {id: rslt.data.fileid, name: rslt.data.filename}
})
})
.catch(err => {
dispatch({
type: GET_ERRORS,
payload: err
})
});
}
Here is the reducer
import {SYNC_CLOUD, UPLOAD_FILE} from './../actions/types';
const initialState = {
files: {}
};
export default function(state=initialState, action){
switch(action.type){
case SYNC_CLOUD:
return{
...state,
files: action.payload
};
case UPLOAD_FILE:
return{
...state,
files: state.files.data.concat(action.payload),
};
default:
return state;
}
}
error screenshot
I think your reducer is wrongly mapped.The key here is to declare inital state as empty files rather empty object. And also concatinating the existing array with the response from api(payload).
Change ur reducer to something like this:
import {SYNC_CLOUD, UPLOAD_FILE} from './../actions/types';
const initialState = {
files: []
};
export default function(state=initialState, action){
switch(action.type){
case SYNC_CLOUD:
return{
...state,
files: action.payload
};
case UPLOAD_FILE:
return{
[...state, action.payload],
};
default:
return state;
}
}
And change the render function to
<tbody>
{this.props.cloud.files.map(file =>(
<tr>
<td>{file.id}</td>
<td>{file.name}</td>
</tr>
))}
</tbody>
mapDispatchToProps parameter to connect needs to be a function. Either do something like this
const mapDispatchToProps= dispatch => bindActionCreators(
{ logoutUser, syncCloud, uploadFile },
dispatch
)
Or, in pass null as your mapDispatchToProps and in your component use the action creator like this this.props.dispatch(uploadFile(data))