REACT Array True or False Inputs? - arrays

Im having difficulty serializing my inputs and saving it on my API. I cant manage to make it work.
Need some advice or inputs how to do this with arrays or even another simpler way.
I having problem how to put the radio button input array data into my postData that will be save to my API
postData is questionid, questiontype and answers
Questions:
1.Earth is flat? True False
2.Earth is round? True False
so my input is radio button like this
handleTF(event) {
const tfc = event.target.value;
console.log(event.target.name)
console.log(tfc);
this.setState({ [event.target.name]: tfc});
}
const tfcs = ["True", "False"]
{tfcs.map((tfc, index) => (
<label key={index}>
{tfc}
<input
name={qd.question.id}
value={tfc}
onChange={this.handleTF}
type="radio"
/>
</label>
))}
handleSubmitTF(event){
event.preventDefault();
const {
questionId,
questionType,
answer
} = this.state;
const postData = {
questionId,
questionType,
answer,
};
let sessionToken = sessionStorage.getItem('session');
let sessToken = sessionToken.replace(/\"/g, "");
fetch('sample.net', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'token'
},
body: JSON.stringify([postData]),
})
.then(response => response.json())
.then(response => {
console.log(response.title)
if(response.title === "Error"){
this.addNotification('danger', 'Danger', 'Already answered', 'top-right')
}else{
this.addNotification('success', 'Success', 'All Data is Saved', 'top-right')
this.componentDidMount();
}
})
.catch(err => {
console.log("fetch error" + err);
});
}

Context
It seems that your state is made of:
{ [event.target.name]: "True" | "False" }
But then you access it with:
const {
questionId,
questionType,
answer
} = this.state;
They do not match type / interface. In TypeScript terms your State looks like:
interface State {
[k: string]: "True" | "False";
}
Solutions
A. Send your state directly
As it has a valid structure, it would be easy to parse on the API side.
const postData = { ...this.state };
B. Send an Array of answers
Otherwise, if you need it as an Array of answers. You can transform it like:
const questions = Object.keys(this.state);
const postData = questions.map((question) => ({
question,
answer: this.state[name],
}));
Side note: Earth is flat, and it is called Discworld =o)

Related

Can't increasing the value in front end by react. Data comes from mongodb

I can't update the value in the front end. I want to decrease the value of quantity when I click delivered.
Here is my code.
const handleDelivered = () => {
const newQuantity = parseInt(inventory.quantity) + 1;
const makeQuantity = newQuantity;
console.log(makeQuantity);
fetch(`http://localhost:4000/inventory/${id}`, {
method: "PUT",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({ makeQuantity }),
})
.then((res) => res.json())
.then((data) => {
console.log(data);
});
};
Here is the update operation on the server-side.
app.put("/inventory/:id", async (req, res) => {
const id = req.params.id;
const data = req.body;
const filter = { _id: ObjectId(id) };
const options = { upsert: true };
const updateDoc = {
$set: {
...data,
},
};
const result = await fruitsCollection.updateOne(
filter,
updateDoc,
options
);
res.send(result);
});
Here is the code for button:
<Card.Link
onClick={handleDelivered}
className="btn btn-danger">
Delivered
</Card.Link>
Here is the screenshot:
If I click the delivered button, the console says it is 13, but UI doesn't update. Also, Restock Item button doesn't increase the quantity
How can I solve this?
Thank you
React doesn't automatically handle model updates for you. You need to handle this yourself or use a library which handles it for you.
Without seeing more code, it's hard to give specific details on how you should update your local models, but it might look like this:
const [inventory, setInventory] = useState(defaultInventory)
function handleDelivered() {
fetch(...).then(res => res.json()).then(setInventory)
}
assuming the response from the fetch is the entire fruit object. If it's a partial, you'll need to merge in the response with the local model.

map axios response then use it for another response

I want to map an array of images, but because Directus is identifying an image or a file as an id in the collection's item.
right now I need to map the array of id from the response in the first request then using that id for making another get request in another endpoint which is right here is files.
the database that I use is DirectusCms, and the front end is react.js
class ComponentToPrint extends React.Component {
constructor(props) {
super(props);
this.handleLoad = this.handleLoad.bind(this);
this.state = {
nama: [],
kk:[],
ijazah:[]
};
}
componentDidMount() {
window.addEventListener('load', this.handleLoad);
}
componentWillUnmount() {
window.removeEventListener('load', this.handleLoad)
}
handleLoad() {
let nama_1;
let id_kk;
const url = `https://sensor/public/gemaclc/items/pendaftar?access_token=sensor`
const url2 = `https://sensor/public/gemaclc/files/${id_kk}?access_token=sensor`
axios(url, {
method: 'get',
headers:{ 'Content-Type': 'application/json'} })
.then(res => {
const nama = res.data.data;
const nama = res.data.data;
console.log( nama)
this.setState( {nama} );
})
.catch(error => console.error('Error:', error))
// .then(res => {console.log('Success:', nama)})
// this.setState( nama );
// console.log(nama)
axios(url2,{
method:`get`,
headers:{ 'Content-Type': 'application/json'} })
.then(res => {
id_kk = res.data.data;
console.log( id_kk)
// this.setState( {id_kk} );
})
}
render() {
return (
<>
{this.state.nama.map(node =>
<div key={node.id}>
<h2>Nama :{node.nama}</h2>
<h2>Tanggal lahir : {node.tanggal_lahir}</h2>
<h2>Tempat lahir : {node.tempat_lahir}</h2>
<h2>Email : {node.email}</h2>
<h2>Email : {node.telepon}</h2>
<h2>kk : {node.kartu_keluarga}</h2>
</div>
)}
</>
);
}
}
When requesting from the items endpoint in Directus one can supply a query parameter called fields, with this you can get nested information from relations, including the hash etc, see: https://docs.directus.io/api/items.html#fields-optional
If you have the private hash, you can use the assets endpoint to get the actual image, see: https://docs.directus.io/api/assets.html#get-an-asset
PS: You haven't mentioned what your problem is, so I guessed it based on my overall knowledge of the topic - next time also mention your exact problem, what you've tried and what didn't work etc

Upload an image with reactjs and mongodb

I'm trying to make an image uploader, thanks to a form, in reactjs.
I've created an api in mongodb (thanks to express, mongoose, etc.), and i'm trying to use it in order to upload an image.
Actually, i would like to send an image file to the cloud (with Cloudinary), and get the url.
That is my form and methods :
class Header extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
data: [],
uploading: false,
image: [],
apiKey: 'xxx'
};
}
onChangeImage = e => {
this.setState({[e.target.name]: Array.from(e.target.files)});
};
sendImage = files => {
const formData = new FormData();
files.forEach((file, i) => {
formData.append(i, file)
});
fetch('http://localhost:3000/image-upload', {
method: 'POST',
headers : new Headers({
'Content-Type' : 'application/x-www-form-urlencoded',
'x-access-token' : this.state.apiKey
}),
body: formData
})
.then(res => res.json())
.then(image => {
this.setState({
uploading: false,
image
});
return true;
});
return false;
};
handleSubmit = (event) => {
event.preventDefault();
const { image } = this.state;
this.sendImage(image);
};
render() {
return(
<form onSubmit={this.handleSubmit} className="formAdd">
<input type='file' id="image" name="image" onChange={this.onChangeImage} />
<button className="contact-form-btn">
Send<i className="fa fa-long-arrow-right" aria-hidden="true"></i>
</button>
</form>
)
}
About my API Controller :
const cloudinary = require('cloudinary');
module.exports = {
create: function(req, res) {
cloudinary.config({
cloud_name: 'xxxx',
api_key: 'xxxxx',
api_secret: 'xxxxx'
});
const path = Object.values(Object.values(req.body.files)[0])[0].path;
cloudinary.uploader.upload(path)
.then(image => res.json([image]));
},
};
The error code that I get is 500 'TypeError: Cannot convert undefined or null to object'.
Indeed, it not found Object.values(Object.values(req.body.files)[0])[0].path.
What I've missed ?
Thanks.
You can use this to upload an image. Using async/await.
async uploadImage(image) {
const form = new FormData();
form.append('file', image);
form.append('upload_preset', 'g5ziunzg');
const res = await Axios.post('YOUR_CLOUDINARY_URL', form)
console.log(res)
return res.data;
}
This will return an object with the secure_url which you can store in your mongo database. I am assuming you have a backend-api for this task.
Inside your formSubmit function, you can first call this function and receive this secure_url.
Note that I am using axios here. This example can easily be translated to work with fetch.
You don't need to use Object.value since req.body.files is an array and you need to check its length before access. Try it:
const [file] = req.body.files
if (file) {
// your logic here
}

Is there a way to get the state to update at the right time?

I successfully send a post request, and the entry enters the database. I want the app to re-render to show the new entry in the table. However the setState hits on the addForm Variable and the form vanishes but it does not re-render again to show the new character, I know this is due to asynchronicity, just unsure on how to make that work in the right order.
So far I have attempted:
- to make sure the promise is returned - no change in behaviour
adding a third .then to setState again to try and force a re-render - no change in behaviour
tried forcing with this.forceUpdate - no change in behaviour
All research shows setState as the way to fix this but having no success. I now wonder if my syntax is wrong or poorly constructed.
The Fetch request
handleSubmit = (character) => {
console.log(character);
const url = "http://localhost:3000//api/v1/basics"
const body = JSON.stringify(
{ name: character.name,
age: character.age,
sex: character.sex,
classs: character.classs,
race: character.race,
height: character.height
})
fetch(url,{
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
body: body
}).then((res) =>{
return res.json();
}).then((res) => {
this.setState({ character: res })
}).then((res) => {
console.log(this.state);
this.setState({ addForm: false })
})
}
I am expecting the component to re-render with the new entry but no re-render happens after the form closes. If i refresh the page then it updates but not onclick.
EDIT 1. : FUlly component, I know its a mess this is a play with react and see what it does program.
import React, { Component } from 'react';
import Table from './Table'
import SimpleCharacterInfoForm from './SimpleCharacterForm'
import CharacterSkillsForm from './characterSkillsForm'
import './App.css'
export default class App extends Component {
state = {
addForm: false,
editForm: false,
character: []
}
addCharacter = () => {
this.setState({
addForm: true
})
}
removeCharacter = index => {
const url = `http://localhost:3000//api/v1/basics/${index}`
fetch(url,{
method: 'DELETE'
}).then((res) => {
res.json()
}).then((res) => {
this.setState({})
})
}
handleSubmit = (character) => {
console.log(character);
const url = "http://localhost:3000//api/v1/basics"
const body = JSON.stringify(
{ name: character.name,
age: character.age,
sex: character.sex,
classs: character.classs,
race: character.race,
height: character.height
})
fetch(url,{
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
body: body
}).then((res) =>{
return res.json();
}).then((res) => {
this.setState({ addForm: false })
})
}
render() {
const {characters, addForm, editForm, character} = this.state;
let render = ''
if (addForm === true){
render = this.renderAddCharacter(characters)
} else if (editForm === true) {
render = this.renderEditCharacterSKills(character)
} else {
render = this.renderWithOutForms(characters)
}
return render
}
renderAddCharacter(characters){
return(
<div className="characterTable">
<Table
characterData={characters}
removeCharacter={this.removeCharacter}
editCharacter={this.editCharacter}
/>
< SimpleCharacterInfoForm
handleSubmit={this.handleSubmit}
/>
<button onClick={this.addCharacter}>Add Character</button>
</div>
)
}
renderEditCharacterSKills(character){
return(
<div className="characterSkillsForm">
<CharacterSkillsForm
handleEdit={this.handleEdit}
character={character}/>
</div>
)
}
renderWithOutForms(characters){
return(
<div className="characterTable">
<Table
characterData={characters}
removeCharacter = {this.removeCharacter}
editCharacter={this.editCharacter}
/>
<button onClick={this.addCharacter}>Add Character</button>
</div>
)
}
}

Fetching a data text from API response and presenting it in a chat with React

I have a chat UI in react in which the user should input text and receive some data from an API response service. The UI works fine and the user text is presented in the chat but the response from the API which received in JSON format doesn't presented in the chat at all (not even an error message from the API, which should appear in the 'message' field,which is the same field where the result should be presented).As far As I know I configured everything ok, I don't know if something is messing in the render method though.
Note: the preconfigured messages at the chat state in the beginning are just for testing purposes.
The Fetch operation in under the componentDidUpdate() method at the first block of code that I posted.
import React from "react";
import ReactDOM from "react-dom";
import "./App.css";
import Message from "./Message.js";
class Chatroom extends React.Component {
constructor(props) {
super(props);
this.state = {
chats: [
{
username: "clientUser",
content: <p>Hello World!</p>,
img:
"http://***.jpg"
},
{
username: "user2",
content: <p>Hi,my name is user2.What's up ??</p>
},
{
username: "user3",
content: <p>Hi,my name is user3.What's up ??</p>
},
{
username: "user4",
content: <p>Hi,my name is user4.What's up ??</p>
},
{
username: "userN",
content: <p>Hi,my name is userN.What's up ??</p>,
img: "http://***.jpg"
},
{
username: "user5",
content: <p>Hi,my name is user5.What's up ??</p>
},
{
username: "user6",
content: <p>Hi,my name is user6.What's up ??</p>
},
{
username: "user7",
content: <p>Hi,my name is user7.What's up ??</p>
}
]
};
this.submitMessage = this.submitMessage.bind(this);
}
componentDidMount() {
this.scrollToBot();
}
componentDidUpdate() {
this.scrollToBot();
fetch(
"https://****",
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
inputText: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>
})
}
).then(response => response.json())
.then(parsedJSON =>
parsedJSON.results.map((
user
) => ({
username: "BotResponse",
content: `${user.message}',
img: "http://***.jpg"
}))
)
.then(chats =>
this.setState({
chats
})
)
.catch(error => console.log("parsing failed", error));
}
scrollToBot() {
ReactDOM.findDOMNode(this.refs.chats).scrollTop = ReactDOM.findDOMNode(
this.refs.chats
).scrollHeight;
}
The submit message method:
submitMessage(e) {
e.preventDefault();
this.setState(
{
chats: this.state.chats.concat([
{
username: "clientUser",
content: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>,
img: "http://***.jpg"
}
])
},
() => {
ReactDOM.findDOMNode(this.refs.msg).value = "";
}
);
}
The render method:
render() {
const username = "clientUser";
const { chats } = this.state;
return (
<div className="chatroom">
<h3>
Title
</h3>
<ul className="chats" ref="chats">
{chats.map((
chat //Defines message component
) => (
<Message chat={chat} user={username} />
))}
</ul>
<form className="input" onSubmit={e => this.submitMessage(e)}>
<input type="text" ref="msg" />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
export default Chatroom;
The problem lies in the way you think. You are thinking like if you were writing plain HTML with JS enhancements. React is a fundamental concept shift. You need to forget a lot of what you knew from regular, vanilla JS, to learn React. In React, you don't get HTMLElement's to do operations but rather work with local/global state and component props to declare your UI. The resulting code is more performant, maintainable and glitch-free.
To get back to your actual issue, you shouldn't be using ref to get the value of your input. Instead, use onChange to set state of your message and retrieve it later when needed.
For example:
export class MessageComposer extends React.Component {
state = {
fields: {
message: ""
}
}
clearField = fieldName => {
this.setState({
fields: {
[fieldName]: ""
}
})
}
onInputChange = e => {
this.setState({
fields: {
[e.target.name]: e.target.value
}
})
}
sendMessage = () => {
const { fields: { message } } = this.state
// Here you send the message contained into `message`.
console.log(message)
// Then you clean the message input value.
this.clearField("message")
}
render () {
const { fields: { message } } = this.state
return (
<div className="MessageComposer__container">
<input
type="text"
name="message"
value={message}
onChange={this.onInputChange}
/>
<button onClick={this.sendMessage}>
Submit
</button>
</div>
)
}
}
EDIT: Just saw that your fetch code includes JSX. WTF?
Try by changing the following:
body: JSON.stringify({
inputText: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>
})
with JSX-free version. It doesn't make any sense to do pass a React component as JSON.stringify isn't even able to serialize a function (ok you'll receive i.e 'Function' or '[Object object]' into inputText on your backend i guess).
Somthing like this:
body: JSON.stringify({
inputText: ReactDOM.findDOMNode(this.refs.msg).value
})
Also, if for any reason React doesn't find your ref (i.e. component returns null or any other reason), the inputText will either throw or return undefined.
If you refactor your code as I suggested above, you could do this:
body: JSON.stringify({
inputText: this.state.fields.message
})
There is a fair amount to discuss regarding your code. To keep this answer simple and focused, I am just going to address your fetch api call and why you may not be seeing anything on the front end.
First, I suggest looking into Life Cycle Methods in the React Documentation. You can find that here. If that link is broken, just go to React's documentation page and search "LifeCycle Methods".
The reason I bring that up is because you are posting your data every time the component updates not when the user submits their chat message. So, the moment the user does submit you call your onSubmit function which calls setState. Setting the state will update the post. So anywhere in your code, when you make changes to your state or if there are changes to your component's props the DOM will update and componentDidUpdate will be triggered and you will post again.
Place your post api call in your onSubmit function. Update your form from this:
submitMessage(e) {
e.preventDefault();
this.setState(
{
chats: this.state.chats.concat([
{
username: "clientUser",
content: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>,
img: "http://***.jpg"
}
])
},
() => {
ReactDOM.findDOMNode(this.refs.msg).value = "";
}
);
}
to this updated onSubmit function:
submitMessage(e) {
e.preventDefault();
fetch(
"https://****",
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
inputText: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>
})
}
).then(response => response.json())
.then(parsedJSON => {
parsedJSON.results.map((
user
) => ({
username: "BotResponse",
content: `${user.message}',
img: "http://***.jpg"
}))
}
)
.then(chats =>
this.setState({
chats
})
)
.catch(error => console.log("parsing failed", error));
//for the sake of readability and debugging, you might want to store
that obj in a local variable here and put that variable in place of ...[]
this.setState(
{
chats: [
...this.state.chats,
{
username: "clientUser",
content: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>,
img: "http://***.jpg"
}
]
},
() => {
ReactDOM.findDOMNode(this.refs.msg).value = "";
}
);
}
Back-end
You want to confirm that this is actually saving to your db (may seem obvious but double check that it is saving the way you want and that you are returning the correct info. in the correct format on your back-end. This could be where the problem is. Log your response to your back-end console and make sure it is returning everything correctly.
Front-End
Look at how you are setting your state in your fetch call. I'm not really sure how you are sending your data but here is an example of a post fetch call using axios.
axios.post("http://****", {
data: this.state.data,
more: this.state.more,
etc: { example: "of", object: this.state.obj }
})
.then(response => {
if (response.status === 200) {
//do something with response.data
//this.setState({ data: response.data})
}
});
There really isn't anything more I can suggest. I'm not really sure what you are trying to achieve with the source you have provided.
Note
There are a few things that you are doing throughout your code that I'm not sure you need to do. But, I did not address that in this answer because I'm not entirely sure what your project is and your desired results. But this should help with the issue of nothing rendering. If it doesn't, look into how you are setting your state in the fetch api call.
Hope this helps.
EDIT
I don't believe you need to have three .then in your api call. Once you have parsed your json you can set your state there.
EDIT 2
The above source was not giving the desired result. The fetch api call was not returning data to the second then call because the data from the first then call. Here is the updated submitMessage function.
submitMessage(e) {
e.preventDefault();
fetch(
"https://****",
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
inputText: <p>{ReactDOM.findDOMNode(this.refs.msg).value}</p>
})
}
).then(response => {
return response.json() //here was the problem, nothing was being returned
})
.then(parsedJSON => {
parsedJSON.results.map(user => ({
username: "BotResponse",
content: user.message, //this shouldn't need to be a string literal
img: "http://***.jpg"
})
)
}
)
.then(chats =>
this.setState({
chats
})
)
.catch(error => console.log("parsing failed", error));
}

Resources