this is my react js code and I want to connect with my node js API but I don't understand how to that ...!
import React, { useState } from "react";
import Poll from "react-polls";
// import "./styles.css";
/**
* https://stackoverflow.com/questions/65896319/react-js-class-poll-convert-into-react-hooks-poll
*/
// Declaring poll question and answers
const pollQuestion = "Youtube is the best place to learn ?";
const answers = [
{ option: "Yes", votes: 7 },
{ option: "No", votes: 2 },
{ option: "don't know", votes: 1 },
];
const Fakepolls = () => {
// Setting answers to state to reload the component with each vote
const [pollAnswers, setPollAnswers] = useState([...answers]);
// Handling user vote
// Increments the votes count of answer when the user votes
const handleVote = (voteAnswer) => {
setPollAnswers((pollAnswers) =>
pollAnswers.map((answer) =>
answer.option === voteAnswer
? {
...answer,
votes: answer.votes + 1,
}
: answer
)
);
};
return (
<div>
<Poll
noStorage
question={pollQuestion}
answers={pollAnswers}
onVote={handleVote}
/>
</div>
);
};
export default function App() {
return (
<div className="App">
<Fakepolls />
</div>
);
}
It work's fine with
// Declaring poll question and answers
const pollQuestion = "Youtube is the best place to learn ?";
const answers = [
{ option: "Yes", votes: 7 },
{ option: "No", votes: 2 },
{ option: "don't know", votes: 1 },
];
but I want to connect this poll with my API instead of Declaring it ..! this is my api- to get data -> ( router.get("/poll/:pollId", getPoll); //)
exports.getPoll = async (req, res, next) => {
try {
const { pollId } = req.params;
const polls = await Poll.findById(pollId);
if (!polls) throw new Error("no polls found");
res.status(200).json(polls);
} catch (error) {
error.status = 400;
next(error);
}
};
This is a postman image -
and this API for POST data- and my node js code -
exports.votes = async (req, res, next) => {
try {
/**
* 1. get the poll from db
* 2. check if the user already exists in any option
* 3. if user has already selected any option do nothing
* 4. if user has selected any other option remove from that option
* 5. if user does not exist in any option, insert his user id to selected option
*/
const { pollId } = req.params;
let { userId, answer } = req.body;
// get selected poll from db
const poll = await Poll.findById(pollId);
if (answer && poll) {
answer = answer.toLowerCase();
///Finf the Poll
let existingVote = null;
Object.keys(poll.options).forEach((option) => {
// loop on all options, check if the user already exists in any option
if (poll.options[option].includes(userId)) {
existingVote = option;
}
});
if (existingVote == null) {
// if there is no existing vote save it to db
try {
const push = {};
push[`options.${answer}`] = userId;
const update = await Poll.findByIdAndUpdate(
pollId,
{ $push: push },
{ upsert: true }
);
res.status(201).json(update);
} catch (err) {
error.status = 400;
next(error);
}
} else if (existingVote && existingVote.length > 0) {
// check if answer is same as previous, if yes send not modified
if (existingVote.toLowerCase() === answer.toLowerCase()) {
res.status(304).send("Response already saved");
} else {
// delete the previous response and save it in new
if (
Array.isArray(poll.options[existingVote]) &&
poll.options[existingVote].length > 0
) {
// TODO: filtering this is not returning array but 1
poll.options[existingVote] = poll.options[existingVote].filter(
(vote) => vote != userId
);
poll.options[answer] = poll.options[answer].push(userId);
const update = await Poll.findByIdAndUpdate(pollId, {
$set: { options: poll.options },
});
res.status(201).json(update);
}
}
} else {
error = {
status: 500,
message: "Something went wrong",
};
next(error);
}
} else {
error = {
status: 404,
message: "Poll not found",
};
next(error);
}
} catch (error) {
error.status = 400;
next(error);
}
};
this is a POSTMAN image using POST to store data --- >
how can I connect API with react poll
What you'd do is make a fetch() to your /api/polls endpoint inside your Fakepolls component, the URL being exactly as you show in your Postman screenshot. More info on fetch here at the MDN docs.
With the response you get from the endpoint, populate the answers array you component uses. From what I see, it would require a bit of transformation as your answer object is not quite the same as what Poll needs.
Next, upon user action, as well as updating the votes in the UI, you need to make another fetch to your vote endpoint.
Here's your component again with these adjustments. Keep in mind it's untested and the URLs are obviously not real:
import React, { useState, useEffect } from "react";
import Poll from "react-polls";
// import "./styles.css";
/**
* https://stackoverflow.com/questions/65896319/react-js-class-poll-convert-into-react-hooks-poll
*/
const Fakepolls = () => {
// Setting answers to state to reload the component with each vote
const [pollQuestion, setPollQuestion] = useState('');
const [pollAnswers, setPollAnswers] = useState([]);
// Query the actual poll info from the server
useEffect(() => {
fetch('http://your-server/api/polls/you-poll-id')
.then((response) => response.json()) //parse response as json
.then((pollObject) => {
let answerCountDictionary = Object.keys(pollObject.options)
.map(oKey => {
return {
option: oKey,
anwers: pollObject.options[oKey].length
}
}); //iterate over the 'options' properties' keys to get the names and the current votes
setPollAnswers(answerCountDictionary);
setPollQuestion(pollObject.question)
})
.catch((error) => {
console.error('Error:', error);
});
},[]) //adding empty array of dependencies to prevent multiple calls on state change
// Handling user vote
// Increments the votes count of answer when the user votes
const handleVote = (voteAnswer) => {
setPollAnswers((pollAnswers) =>
pollAnswers.map((answer) =>
answer.option === voteAnswer
? {
...answer,
votes: answer.votes + 1,
}
: answer
)
);
//also submit the backend
fetch('http://your-server/api/vote/poll-id', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
"userId": "the-logged-in-user",
"answer": voteAnswer
},
})
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
};
return (
<div>
<Poll
noStorage
question={pollQuestion}
answers={pollAnswers}
onVote={handleVote}
/>
</div>
);
};
export default function App() {
return (
<div className="App">
<Fakepolls />
</div>
);
}
Related
EDIT: Simplified the code a bit by using two separate states. Adding the channel state to the dependency array of useEffect() does not remove the error but does make the application run in a strange manner: If I try to send messages, I see multiple messages.
EDIT 2: If I use the useRef() hook to store the state, my application works albeit in a strange manner again: the state is one step slower than the actual user input.
I'm trying to build a simple chat application using React.js and Socket.IO where a single user can communicate with multiple users. I have an endpoint from where I fetch all my channel data and store it in a state but I'm not able to access the state (returns null) even if the state is being passed down to a different component as props and that is being rendered correctly the first time. When I click on a channel to select it, the channel list is again reset to null and I get this error:
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'forEach')
I'm trying to implement this by following the tutorial from here. It's outdated, so I'm implementing a newer version of it. But even then, if I use the component class defined in the author's github, it seems to work just fine. I have not worked with component classes before, so I am not sure where I am going wrong.
Main component: Chat.js
import React, { useEffect, useState } from 'react';
import ChannelList from './ChannelList';
import './chat.css';
import MessagePanel from './MessagePanel';
import { io } from "socket.io-client";
const SERVER = "http://localhost:8080";
let socket;
function Chat() {
const [channel, setChannel] = useState(null)
const [channels, setChannels] = useState(null) //the state where the channels are loaded
const channelsRef = useRef() //edit 2
useEffect(() => {
loadChannels() //the function which loads the channels into the state
configureSocket()
}, []) //adding channel to the dependency array results in strange behavior.
const configureSocket = () => {
socket = io(SERVER)
socket.on('connection', () => {
if(channel) {
handleChannelSelect(channel.id)
}
});
socket.on('channel', channel => {
console.log('channel details', channel); //returns the correct channel clicked on
//let temp_channels = channels; //edit 2
let temp_channels = channelsRef.current; //edit 2
console.log('channels inside onchannel', temp_channels); //returns null, does not return null if useRef() is used
temp_channels.forEach(c => { //I get the above mentioned error at this line, after edit 2 I don't get any error
if(c.id === channel.id) {
c.participants = channel.participants
}
})
//setChannels(channels); edit 2
channelsRef.current = temp_channels // edit 2
})
socket.on('message', message => {
// let temp_channels = channels
let temp_channels = channelsRef.current //edit 2
temp_channels.forEach(c => {
if (c.id === message.channel_id) {
if (!c.messages) {
c.messages = [message];
} else {
c.messages.push(message);
}
}
})
// setChannels(channels) edit 2
channelsRef.current = temp_channels // edit 2
})
}
const handleSendMessage = (channel_id, text) => {
socket.emit('send-message', { channel_id, text, senderName: socket.id, id: Date.now()})
}
const handleChannelSelect = (id) => {
const channel = channels.find(c => {
return c.id === id;
});
setChannel(channel);
socket.emit('channel-join', id, ack => {});
}
const loadChannels = async () => {
await fetch('http://localhost:8080/getChannels')
.then(async response => {
const data = await response.json()
console.log('data inside loadchannels', data.channels);
setChannels(data.channels); //this sets the channels and passes it down to the child component "ChannelList"
channelsRef.current = data.channels //if setChannels is not used before this, it doesn't work
})
console.log('after fetching channels inside loadchannels', channel, channels); //both channel and channels state are null
}
return (
<div className="chat-app">
<ChannelList channels={channels} onSelectChannel={handleChannelSelect}></ChannelList>
<MessagePanel onSendMessage={handleSendMessage} channel={channel}/>
</div>
);
}
export default Chat;
ChannelList.js
import React from 'react';
import Channel from './Channel';
function ChannelList(props) {
const handleClick = (id) => {
props.onSelectChannel(id);
}
console.log('inside channellist component', props.channels); //returns the set of channels received from props
let list = <div className="no-content-message">There are no channels to show</div>;
if (props.channels && props.channels.map) {
list = props.channels.map(c =>
<Channel
key={c.id}
id={c.id}
name={c.name}
participants={c.participants}
onClick={handleClick}
/>
);
}
return (
<div className='channel-list'>
{list}
</div>
);
}
export default ChannelList;
Backend index.js:
const express = require('express');
const app = express();
http = require('http');
const cors = require('cors');
const { Server } = require('socket.io');
app.use(cors());
const server = http.createServer(app);
const STATIC_CHANNELS = [
{
name: 'Global chat',
participants: 0,
id: 1,
sockets: []
},
{
name: 'Funny',
participants: 0,
id: 2,
sockets: []
},
{
name: 'Test',
participants: 0,
id: 3,
sockets: []
},
];
const io = new Server(server, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST'],
},
});
server.listen(8080, () =>
console.log('Server is running on port 8080')
);
io.on('connection', (socket) => {
console.log(`User connected ${socket.id}`);
socket.emit('connected', socket.id);
socket.on('channel-join', id => {
console.log('channel join', id);
STATIC_CHANNELS.forEach(c => {
if (c.id === id) {
if (c.sockets.indexOf(socket.id) == (-1)) {
c.sockets.push(socket.id);
c.participants++;
io.emit('channel', c);
}
} else {
let index = c.sockets.indexOf(socket.id);
if (index != (-1)) {
c.sockets.splice(index, 1);
c.participants--;
io.emit('channel', c);
}
}
});
return id;
})
socket.on('send-message', message => {
io.emit('message', message);
})
socket.on('disconnect', () => {
STATIC_CHANNELS.forEach(c => {
let index = c.sockets.indexOf(socket.id);
if(index != (-1)) {
c.sockets.splice(index, 1);
c.participants--;
io.emit('channel', c);
}
})
})
});
app.get('/getChannels', (req, res) => {
res.json({
channels: STATIC_CHANNELS
})
})
I want to delete data from UI and Server, I write this code, but the problem is when I click the Delete button data was deleted from UI but still exists in the server. After reloading the page deleted data showed in the UI again. when I console.log(data) the result is { acknowledged: true, deletedCount: 0 }.
/* server side code */
app.delete('/data/:id', async (req, res) => {
const id = req.params.id
const query = { _id: ObjectId(id) }
const result = await dataCollection.deleteOne(query)
res.send(result)
})
/* client side code */
const [data, setData] = useData()
const deleteData = async (id) => {
const confirmation = window.confirm('Are you sure about delete the data ?')
if (confirmation) {
const { data } = await axios.delete(`http://localhost:5000/data/${id}`, product)
const exist = data.filter( x => x._id !== id);
setData(exist)
})
}
The result { acknowledged: true, deletedCount: 0 }
Expected result { acknowledged: true, deletedCount: 1 }
There is some problem with your other code, you have used a custom hook as useData . Your server response indicates that data is deleted from server, try to fetch data again from server and update the ui content with that new data. I am leaving a working version demo below:
function SingleProductMng({ item, products, setProducts }) { // here props are drilled in parent container
// here item is the single item to render
// products and setProducts is custom useState hook to render data from server
const { _id, name, supplier, price, quantity, category } = item;
const handleDelete = () => {
if (window.confirm('Are you sure you want to delete?')) {
console.log('deleted');
fetch(`your server url`, {
method: 'DELETE',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(item),
});
const filteredItem = products.filter((product) => product._id !== _id);
setProducts(filteredItem);
} else {
console.log('cancel');
}
};
return (<>components</>)
I would like to explain my problem of the day.
to put you in context, I'm logging in and I'm on a page like my information or my profile.
I have a funny error, so I'm trying to recover data from. my BDD in order to correctly display the telephone number.
the 1st error is the following on my LIVE server when the page opens I have a 404 error which is displayed
and the second when I make a POSTMAN request
{
code: 'ER_BAD_FIELD_ERROR',
errno: 1054,
sqlMessage: "Unknown column 'undefined' in 'where clause'",
sqlState: '42S22',
index: 0,
sql: 'SELECT * from cartebleuuser where telephone=undefined'
}
I would like to be able to display the correct telephone which corresponds to the UID in my database
How can I fix this issue?
import React, { Component } from 'react';
import axios from 'axios'
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
userProfile: null
};
}
getUserProfile = async (telephone) => {
const res = await axios.get(
`https://joke.fr/api/cartebleuuser${telephone}`
);
this.setState({ userProfile: res.data });
console.log(res.data);
}
componentDidMount() {
const user = localStorage.getItem("authUser");
console.log(user);
if (user) {
const { id, telephone } = JSON.parse(user);
this.getUserProfile(telephone);
}
}
render() {
const { userProfile } = this.state;
return (
<div>
{userProfile ? userProfile.telephone : "No user telephone"}
</div>
)
}
}
export default Profile;
MyRoute on BDD
app.get('/api/cartebleuuser', (req, res) => {
const { telephone } = req.params;
console.log(telephone);
connection.query(`SELECT * from cartebleuuser where telephone=${telephone}`, (err, results)
=> {
if (err) {
console.log(err);
return res.status(500).send('Erreur lors de la récupération des employés');
} else {
console.log(results);
return res.json(results);
}
});
});
BDD schéma.
{
"id": 62,
"telephone": "0202020202",
"uid": "dycjibu96zgmzc0KpGAqxKiUsMu2"
}
I have a MERN stack application that is modified from a great tutorial I completed. In the original app, transactions were rendered in a list populated from an API call to Mongo Atlas DB. I converted the list to a react-data-table-component and am now trying to figure out how to delete a table row/transaction. The original app had this as part of the transaction component with an onClick button. When I attempt to use the deleteTransaction function, I receive a "TypeError: Cannot read property '_id' of undefined". I can see that the data table renders via the object {transactions}, but cannot figure out why it does not recognize the _id.
Other info: state is managed through the React Context API, with a Router.js and Reducer.js.
TransactionTable.js
import React, { useContext, useEffect } from "react";
// Data table imports
import IconButton from "#material-ui/core/IconButton";
import DeleteIcon from "#material-ui/icons/Delete";
import Card from "#material-ui/core/Card";
import DataTable from "react-data-table-component";
// import transaction component and context provider
import { GlobalContext } from "../context/GlobalState";
// create data table component
export const TransactionTable = () => {
const { transactions, getTransactions, deleteTransaction } = useContext(
GlobalContext
);
// react-data-table-component Columns for back-end data
const columns = [
{
name: "Transaction",
selector: "text",
sortable: true
},
{
name: "Amount",
selector: "amount",
sortable: true,
// conditionally render amount if positive or negative
conditionalCellStyles: [
{
when: row => row.amount > 0,
style: {
color: "green"
}
},
{
when: row => row.amount < 0,
style: {
color: "red"
}
}
]
},
{
// where I'm attempting to pass the transactions prop and apply the deleteTransaction function
// using the delete button that renders in each row
cell: ({ transactions }) => (
<IconButton
aria-label="delete"
color="secondary"
onClick={() => deleteTransaction(transactions._id)}
>
<DeleteIcon />
</IconButton>
)
}
];
useEffect(() => {
getTransactions();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div>
<Card style={{ height: "100%" }} p={2} mx="auto">
<DataTable
title="Transactions"
columns={columns}
data={transactions}
defaultSortField="Transactions"
//actions={actions}
pagination={true}
highlightOnHover={true}
dense={true}
/>
</Card>
</div>
);
};
./controllers/transactions.js - this is where the deleteTransaction function is
const Transaction = require('../models/Transaction');
// #desc Get all transactions
// #route GET /api/v1/transactions
// #access Public
exports.getTransactions = async (req, res, next) => {
try {
const transactions = await Transaction.find();
//const result = result.transaction.toString()
return res.status(200).json({
success: true,
count: transactions.length,
data: transactions
});
} catch (err) {
return res.status(500).json({
success: false,
error: 'Server Error'
});
}
}
// #desc Add transaction
// #route POST /api/v1/transactions
// #access Public
exports.addTransaction = async (req, res, next) => {
try {
const { text, amount } = req.body;
const transaction = await Transaction.create(req.body);
return res.status(201).json({
success: true,
data: transaction
});
} catch (err) {
if(err.name === 'ValidationError') {
const messages = Object.values(err.errors).map(val => val.message);
return res.status(400).json({
success: false,
error: messages
});
} else {
return res.status(500).json({
success: false,
error: 'Server Error'
});
}
}
}
// #desc Delete transaction
// #route DELETE /api/v1/transactions/:id
// #access Public
exports.deleteTransaction = async (req, res, next) => {
try {
const transaction = await Transaction.findById(req.params.id);
if(!transaction) {
return res.status(404).json({
success: false,
error: 'No transaction found'
});
}
await transaction.remove();
return res.status(200).json({
success: true,
data: {}
});
} catch (err) {
return res.status(500).json({
success: false,
error: 'Server Error'
});
}
}
According to the docs https://www.npmjs.com/package/react-data-table-component#custom-cells, each cell is passed an object named row by convention (you can name it to whatever you want)..
This row object should have the _id you need..
// react-data-table-component Columns for back-end data
const columns = [
// ... column items,
{
cell: row => (
<IconButton
aria-label="delete"
color="secondary"
onClick={() => deleteTransaction(row._id)}
>
<DeleteIcon />
</IconButton>
)
}
]
Each row basically represents a single transaction.
I am using vuejs (CLI 3) with axios and sockets. My backend is NodeJs.
Html (view all users):
...
<ul v-if="users.length > 0">
<li v-for="user in users" :key="user.id">
<router-link :to="'/oneuser/' + user.permalink" tag="li" active-class="active" #click.native="setmyid(user._id)">
<a>{{ user.name }} - {{ user.last_name }}</a>
</router-link>
</li>
</ul>
...
<script>
import axios from 'axios'
import io from 'socket.io-client'
export default {
name: 'get-user',
data () {
return {
users: [],
socket: io('localhost:7000')
}
},
methods: {
mycall () {
axios.get('http://localhost:7000/api/users')
.then(res => {
// console.log(res)
const data = res.data
const users = []
for (let key in data) {
const user = data[key]
user.id = key
users.push(user)
}
// console.log(users)
this.users = users
})
.catch(error => console.log(error))
}
}
mounted () {
this.mycall()
this.socket.on('user-deleted', function (data) {
this.mycall()
})
}
}
</script>
Html (user view):
<script>
import axios from 'axios'
export default {
name: 'one-user',
data () {
return {
name: '',
surname: '',
id: localStorage.getItem('usId'),
socket: io('localhost:7000')
}
},
mounted () {
axios.get('http://localhost:7000/api/get-user/' + this.id)
.then(res => {
const data = res.data
this.name = data.name
this.surname = data.last_name
})
.catch(error => console.log(error))
},
methods: {
mySubmit () {
const formData = {
_id: this.id
}
axios.post('http://localhost:7000/api/delete-user', formData)
.then(this.$router.push({ name: 'get-user' }))
.catch(error => console.log(error))
}
}
}
</script>
backend NodeJs:
controller.postDeleteUser = function(req,res){
User.deleteOne({"_id" : req.body._id}, function(err){
io.emit('user-deleted', req.body._id);
res.send('ok');
});
};
When I go to user view and delete the user then it directs me to view all users. I have two major problems here.
1) After redirect, I saw again all the users even the deleted one. In my database the user has deleted correctly.
2) I don't know where exactly and how to use sockets in my code.
I am using in the front the socket.io-client npm plugin. Also I don't want to use (and I don't use it in my code) vue-socket.io because IE 11 and below version are not supported and it throws me some errors.
What I have tried so far:
1) Using watch like this:
watch: {
users: function (newValue) {
newValue = this.mycall()
}
}
This is very bad for browser performance, because always call request from the browser.
2) use beforeUpdate or Updated life-cycle:
updated () {
this.mycall()
}
That works but has bad performance. It makes requests many times to the server.
3) or that with sockets
updated () {
this.socket.on('user-deleted', function (data) {
this.mycall()
})
}
and that throws me an error:
this.mycall() is not a function
What am I doing wrong?
Where to put the code with sockets?
I have changed the view all users file to:
...
methods: {
mycall () {
axios.get('http://localhost:7000/api/users')
.then(res => {
const data = res.data
const users = []
for (let key in data) {
const user = data[key]
user.id = key
users.push(user)
}
this.users = users
})
.catch(error => console.log(error))
},
socketcall (thecall) {
this.socket.on(thecall, (data) => {
this.mycall()
})
}
}
....
created () {
this.mycall()
},
mounted () {
this.socketcall('user-deleted')
}
Life cycle-hooks cannot retrieved functions inside "this.socket.on" so I thought to do like above and it works!