update row on supabase with Sveltekit - sveltekit

I'm trying to update some data in differents row but, for some reason its not working.
This is what I'm trying to do:
<script>
import { page } from '$app/stores';
import supabase from '../../lib/db';
let id = $page.params.id;
let title, content, description;
const updatePost = async (title, description, content) => {
const { data, error } = await supabase
.from('posts')
.update({ title, description, content })
.eq('id', id);
return { data, error };
};
</script>
<form action="" on:submit|preventDefault={updatePost}>
<input type="text" bind:value={title} />
<input type="text" bind:value={description} />
<input type="text" bind:value={content} />
<button>Send</button>
</form>
I don't have any error but its not updating. I just want to understand why. Everything else is working (create, delete, get).

Well, I found the problem. There is no parameters to pass. I was overriding the function.
the correct way.
<script>
import { page } from '$app/stores';
import supabase from '../../lib/db';
let id = $page.params.id;
console.log(id);
let title, content, description;
const updatePost = async () => {
const { data, error } = await supabase
.from('posts')
.update({ title, description, content, updated_at: new Date() })
.eq('id', id);
return { data, error };
};
</script>
<form action="" on:submit|preventDefault={updatePost}>
<input type="text" bind:value={title} />
<input type="text" bind:value={description} />
<input type="text" bind:value={content} />
<button>Send</button>
</form>

Related

setState on submit and not onChange

I have a form, where I use the input from the input fields to send to a back-end.
For example, I have a variable looking something like this:
const [data, setData] = useState([])
const [inputField, setInputField] = useState()
Then I have a form that looks something like this:
<form onSubmit={fetchData}>
<input type="number" value={value} onChange={(e) => setInputField(e.target.value)} />
<button type="submit">Fetch data</button>
</form>
The fetchData is given by:
function fetchData(e?: any) {
e?.preventDefault();
POST("/api", {
inputField: inputField,
}).then(async (response) => {
const json = await response.json();
setData({
retrievedData: json.retrievedData,
});
});
}
I have other forms as well, where this onChange updating is good, but for some input fields I don't need it do update/re-render before the actual submit button that triggers the form are clicked.
So how do I update the state of the inputField when the button is clicked, instead of now where it updates every time I write a new character in the input field ?
Try this
import {useRef } from "react";
export const Temp = () => {
const inputField = useRef(null);
const onBtnClick = () => {
alert(inputField?.current?.value);
};
return (
<div>
<input type="text" ref={inputField} />
<button type="submit" onClick={onBtnClick}>
Fetch data
</button>
</div>
);
};
You can use useRef hook for that.
const inputNumber = useRef();
<input
ref={inputNumber}
id="number"
placeholder="33xx"
type="number"
/>
Then on button click you can get the value like that
inputNumber.current?.value,
You don't need a state for that and you don't even need a ref. You can get the form values directly from the submit event (event.target.<input name>.value). You will need to add the name property to the <input /> to make it accessible from the event target. Please, find the example below:
function Form() {
const [data, setData] = React.useState();
const onSubmit = (e) => {
e.preventDefault();
const inputField = e.target.inputField.value;
POST("/api", {
inputField: inputField,
}).then(async (response) => {
const json = await response.json();
setData({
retrievedData: json.retrievedData,
});
});
};
return (
<form onSubmit={onSubmit}>
<input type="text" name="inputField" />
<button type="submit">Submit</button>
</form>
);
}
ReactDOM.render(
<Form />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

React Axios double form submit

I'm building a React + PHP API app which let's visitors of the page subscribe to newsletter. I'm using Hooks, Axios and Typescript as well. I tested the API with Postman and the data gets submitted as it should. But when I do the post from frontend, the data comes over and gets inserted in the database twice - one normal row and an empty row.
My code for frontend
import React, { FormEvent } from 'react';
import { useForm } from "react-hook-form";
import axios from 'axios';
import { BsArrowRight } from 'react-icons/bs';
interface IFormNewsletter {
email: string;
emailProvider: string;
dateCreated: number;
}
const FormNewsletter: React.FC = () => {
const { register, handleSubmit } = useForm<IFormNewsletter>();
const preventDefault = (e: FormEvent) => {
e.preventDefault();
}
const onSubmit = (data: IFormNewsletter) => {
data.emailProvider = data.email.split('#')[1];
data.dateCreated = Math.round((new Date()).getTime() / 1000);
axios.post('http://localhost/mb-backend/api/create', {
email: data.email,
emailProvider: data.emailProvider,
dateCreated: data.dateCreated
})
.then((response) => {
console.log(response);
}, (error) => {
console.log(error);
});
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="input-box">
<input ref={register} className="newsletter" type="text" name="email" id="email" placeholder="Type your email address here…" />
<button type="submit" className="submit" name="submit" id="submit"><BsArrowRight className="submit-arrow" /></button>
</div>
<div className="tos-box">
<label className="check-container">
<input type="checkbox" />
<span className="checkmark"></span>
</label>
<span className="tos-label">I agree to terms of service</span>
</div>
</form>
)
}
export default FormNewsletter;
This is how the post gets inserted:
What I tried is to use the preventDefault method, but it stops the form from submitting at all:
<form onSubmit={(e) => {
e.preventDefault();
handleSubmit(onSubmit);
}}>
I also tried to use fetch instead of axios but the result is the same. I'm new to React and would appreciate the help.
EDIT:
The backend looks like this:
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Access-Control-Allow-Headers,Content-Type,Access-Control-Allow-Methods,Authorization,X-Requested-With');
include_once '../config/Database.php';
include_once '../models/User.php';
$database = new Database();
$db = $database->connect();
$user = new User($db);
$data = json_decode(file_get_contents("php://input"));
$user->dateCreated = $data->dateCreated;
$user->email = $data->email;
$user->emailProvider = $data->emailProvider;
if ($user->create()) {
echo json_encode(
array('message' => 'User created')
);
} else {
echo json_encode(
array('message' => 'Could not create user')
);
}
add disabled={formState.isSubmitting}>
<button type="submit" disabled={formState.isSubmitting}> className="submit" name="submit" id="submit"><BsArrowRight className="submit-arrow" /></button>

How do I join the file url field (Firestore storage) with the other fields in Firestore? (react)

I've trying to:
Upload image to firebase storage
create field 'fileUrl' in Firestore under the 'articles' document
reference the doc in Firestore so that each image knows to which article it is assigned.
I managed to do all of that in a way but the issue is that when I submit the form with a title, content and image, it creates a new document in Firestore with just a the image ('fileUrl'), instead of creating a document that includes the rest of the form's data.
I know that this is probably because in my UploadFile.js I'm creating a new document but how do I join the Field created in UploadFile.js with the fields in AddArticle.js?
I'm also getting ''Unhandled Rejection (TypeError): Cannot read property 'state' of undefined' for this line:
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
UploadFile.js
import React from "react";
import firebase from '../Firebase';
function UploadFile() {
const [fileUrl, setFileUrl] = React.useState(null);
const onFileChange = async (e) => {
const file = e.target.files[0]
const storageRef = firebase.storage().ref()
const fileRef = storageRef.child(file.name)
await fileRef.put(file);
setFileUrl(await fileRef.getDownloadURL().then(fileUrl => {
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
.then(() => {
setFileUrl('')
})
} ));
};
return (
<>
<input type="file" onChange={onFileChange} />
<div>
<img width="100" height="100" src={fileUrl} alt=''/>
</div>
</>
);
}
export default UploadFile;
AddArticle.js
import React, { Component } from 'react';
import firebase from '../Firebase';
import UploadFile from '../components/UploadFile';
class AddArticle extends Component {
constructor() {
super();
this.ref = firebase.firestore().collection('articles');
this.state = {
title: '',
content: '',
fileUrl: ''
};
}
onChange = (e) => {
const state = this.state
state[e.target.name] = e.target.value;
this.setState(state);
}
onSubmit = (e) => {
e.preventDefault();
const { title, content, fileUrl } = this.state;
this.ref.add({
title,
content,
fileUrl
}).then((docRef) => {
this.setState({
title: '',
content: '',
fileUrl: '',
documentId: docRef.id
});
this.props.history.push("/")
})
.catch((error) => {
console.error("Error adding document: ", error);
});
}
render() {
const { title, content, fileUrl } = this.state;
return (
<div className="container">
<br></br><br></br><br></br>
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title text-center">
Create a new article
</h3>
</div>
<br></br><br></br>
<div className="panel-body">
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label for="title">Title:</label>
<input type="text" className="form-control" name="title" value={title} onChange={this.onChange} placeholder="Title" />
</div>
<div className="form-group">
<label for="content">Content:</label>
<textArea className="form-control" name="content" onChange={this.onChange} placeholder="Content" cols="80" rows="20">{content}</textArea>
</div>
{/* <input type="file" onChange={this.onFileChange} /> */}
<UploadFile onChange={this.onChange} value={fileUrl}/>
<button type="submit" className="btn btn-success">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
export default AddArticle;
Each time you call firebase.firestore().collection('articles').add(...), you add a new document to the database. Since you call that in both AddArticle and in UploadFile, your image URL and the other fields indeed will end up in separate documents.
The way you're handling the file upload is a bit unusual to me, as I'd normally expect that upload to happen when the other fields are also submitted. Right now, it's not clear to me what the image belong to, as J.A.Hernández also commented.
Either way: you'll need to remove one of the firebase.firestore().collection('articles').add(...) calls and instead pass the document ID into that call. Say that you first add the text fields and then upload the image, the UploadFile can then update the document with:
firebase.firestore().collection('articles').doc(documentId).update({
fileUrl: fileUrl
})
One way to pass the document ID from the to is by keeping it in the state:
this.ref.add({
title,
content,
fileUrl
}).then((docRef) => {
this.setState({
title: '',
content: '',
fileUrl: '',
documentId: docRef.id
});
this.props.history.push("/")
})
And then:
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})

ReactJs - Axios : Uploading Image

I hope you are well especially in the covid crisis.
im trying to upload an image using axios but it apears always to be null and i cant fix it.
i used encType="multipart/form-data" , and <meta name="csrf-token" content="{{ csrf_token() }}" and nothing works for me ; i think the problem is within the onChange{} despite its from the official ReactJs documentation.
here my component :
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
export default class MedcineRegister extends Component
{
constructor(props)
{
super(props);
this.state = {
json:JSON.parse(props.data),//data received from a laravel controller used to implement the select option menu down below.
Specialite: '1',//initialization
image: '',
};
this.onChangeValue = this.onChangeValue.bind(this);
this.onSubmitButton = this.onSubmitButton.bind(this);
}
onChangeValue(e) {
this.setState({
[e.target.name]: e.target.value,//->this line is working only for Specialite
});
}
async onSubmitButton(e) {
e.preventDefault();
try {
const response = await axios.post('/medcine/registerR',{
Specialite: this.state.Specialite,
image: this.state.image,
});
console.log(response.data);//[{…}]0: {Specialite: "8" , image: null}
} catch (error) {
console.log("error in MedcineRegister.js");
}
}
componentDidMount () {
}
render()
{
return (
<div className="container">
<div className="card-body">
<form encType="multipart/form-data" onSubmit={this.onSubmitButton}>
<div className="col-md-6">
<select onChange={this.onChangeValue} name="Specialite" value={this.state.value} autoFocus>
{this.state.json.map(i => (
<option className="form-control" value={i.id}>{i.nom}</option>
))}
</select>
</div>
<div className="col-md-6">
<input id="file" type="file" name="file" onChange={this.onChangeValue} autoFocus/>
</div>
<div className="form-group row mb-0">
<button className="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
);
}
}
if (document.getElementById('mr')) {
var data = document.getElementById(('mr')).getAttribute('data');
ReactDOM.render(<MedcineRegister data={data}/>, document.getElementById('mr'));
}
as you see guys the consol is alwas showing me "image" : null , any idea how to solve it please
The name attribute of your file input is "file".
So the this.setState in the onChangeValue is actually:
this.setState({
"file": e.target.value
});
Image is never being set.
And if it's a file that you want to post, there are a few changes to be made.
The setState in onChangeValue function should be:
this.setState({
image: e.target.files[0]
});
Data to be posted has to be sent as formData
const formData = new FormData();
formData.append("Specialite", this.state.Specialite);
formData.append("image", this.state.image);
const response = await axios.post("/medcine/registerR", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
});

The DOM is not updating after my post request with axios in React although the state is updated

I have a very simple basic React application up and running. I can post and get data via axios and I can see in the console that when I post data my state is updated but the DOM does not reflect that and I need to refresh to see the change. Please check what I do wrong:
Here is my get method in componentDidMount:
componentWillMount() {
const { posts } = this.state;
axios
.get("dburl/posts.json")
.then(response => {
const data = Object.values(response.data);
this.setState({ posts : data });
});
}
and this is my post method for form submision. I am creating posts with title and content and show them in the screen:
handleSubmit = event => {
event.preventDefault();
const {post} = this.state;
const {posts} = this.state;
axios
.post("dburl/posts.json", post)
.then(response => {
console.log(response);
const newPost = Object.values(response.data);
this.setState({ post: newPost });
const updatedPosts = posts.push({title:post.title,content:post.content});
console.log(post);
console.log(updatedPosts);
console.log(this.state.posts);
});
};
And here is how I display the data:
render() {
let posts = <p>No posts yet</p>;
if (this.state.posts !== null) {
posts = this.state.posts.map(post => {
return <Post key={post.id} {...post} />;
});
}
return (
<React.Fragment>
{posts}
<form className="new-post-form" onSubmit={this.handleSubmit}>
<label>
Post title
<input
className="title-input"
type="text"
name="title"
onChange={this.handleChange}
/>
</label>
<label>
Post content
<input
className="content-input"
type="text"
name="content"
onChange={this.handleChange}
/>
</label>
<input className="submit-button" type="submit" value="submit" />
</form>
</React.Fragment>
);
}
I don't understand why it is not updating the DOM and the new post is not showing right away. Please check. Thanks.
You are updating your state wrongly,
your code:
console.log(response);
const newPost = Object.values(response.data);
this.setState({ post: newPost });//this will update the state with new values
const updatedPosts = posts.push({title:post.title,content:post.content});//here you are
pushing the new values to the existing posts
console.log(post);
console.log(updatedPosts);
console.log(this.state.posts);
Desired code:
console.log(response);
const newPost = Object.values(response.data);
this.setState({ posts: [...this.state.posts, {title:post.title,content:post.content}]});
console.log(post);
console.log(this.state.posts);
This way it will update your state after updating the posts array.

Resources