Trouble uploading image with InertiaJS, React and Laravel - reactjs

The job is very simple: Take postTitle, postBody and postImage as input and update them in database. But I am facing difficulties uploading the image, hope someone will help me. I am using InertiaJs, React and Laravel.
My EditPost.jsx file:
import {React, useState, useEffect} from 'react'
import Layout from '../Shared/Layout'
import { useForm } from '#inertiajs/inertia-react'
function EditPost({errors, postId, postTitle, postBody, postImage}) {
const { data, setData, put, progress } = useForm({
postTitle: postTitle,
postBody: postBody,
postImage: null
})
const onSubmitHandler = (e) => {
e.preventDefault()
put('/posts/update/' + postId)
}
return (
<div className="container">
<div className="row justify-content-center">
<div className="col-md-8">
<br/>
<br/>
<h3 className="text-center">Edit the post</h3>
<form onSubmit={onSubmitHandler}>
<div className="mb-3">
<label htmlFor="postTitle" className="form-label">Post title</label>
<input value={data.postTitle} onChange={e => setData('postTitle', e.target.value)} type="text" className="form-control" id="postTitle" placeholder="Enter post title here..." />
{errors.postTitle && <p className="text-danger">{errors.postTitle}</p>}
</div>
<div className="mb-3">
<label htmlFor="postBody" className="form-label">Post body</label>
<input value={data.postBody} onChange={e => setData('postBody', e.target.value)} type="text" className="form-control" id="postBody" placeholder="Enter post body here..." />
{errors.postBody && <p className="text-danger">{errors.postBody}</p>}
</div>
<div className="mb-3">
<label htmlFor="postImage" className="form-label">Post body</label>
<input type="file" className="form-control" id="postImage" value={data.postImage} onChange={e => setData('postImage', e.target.files[0])} />
{errors.postImage && <p className="text-danger">{errors.postImage}</p>}
</div>
{progress && (
<progress value={progress.percentage} max="100">{progress.percentage}%
</progress>
)}
<br/>
<button className="btn btn-success rounded">Submit</button>
</form>
</div>
</div>
</div>
)
}
EditPost.layout = page => <Layout pageTitle={'Edit the Post'} children={page}/>
export default EditPost
I am following the InertiaJS documentation but getting this warning in console:
Warning: `value` prop on `input` should not be null. Consider using an empty string to clear the component or `undefined` for uncontrolled components.
[.......]
If I ignore the error and select file, I get the following errors:
Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
react_devtools_backend.js:2556 Warning: A component is changing an uncontrolled input of type file to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
Please help me out...

Try using POST instead of PUT, otherwise you would need to spoof the method.
Also if the version of Inertia you are using is below 0.8.0, you will need to pass the information as a FormData object.
Uploading files using a multipart/form-data request is not natively supported in some languages for the put, patch or delete methods. The workaround here is to simply upload files using post instead.
Check out the page on Inertia file uploads here which has a React example.

Related

upload file with inertia react and laravel 9

im have problem with upload file with inertia react and laravel 9
i use this exact code but when i choose a file i got errors in console
https://inertiajs.com/file-uploads
Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
input
form
Create#http://127.0.0.1:5173/resources/js/Pages/Admin/Setting/Content/ContactUs/Create.jsx?t=1659389601569:28:14
s#http://127.0.0.1:5173/node_modules/.vite/deps/#inertiajs_inertia-react.js?v=0d793d15:741:16
The above error occurred in the <input> component:
input
form
Create#http://127.0.0.1:5173/resources/js/Pages/Admin/Setting/Content/ContactUs/Create.jsx?t=1659389680993:28:14
s#http://127.0.0.1:5173/node_modules/.vite/deps/#inertiajs_inertia-react.js?v=0d793d15:741:16
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
this is my code
import React from "react";
import Button from "#mui/material/Button";
import { Link, useForm, usePage } from "#inertiajs/inertia-react";
export default function Create() {
const { data, setData, post, progress } = useForm({
name: null,
avatar: null,
});
function submit(e) {
e.preventDefault();
post("/users");
}
return (
<form onSubmit={submit}>
<input
type="text"
value={data.name}
onChange={(e) => setData("name", e.target.value)}
/>
<input
type="file"
value={data.avatar}
onChange={(e) => setData("avatar", e.target.files[0])}
/>
{progress && (
<progress value={progress.percentage} max="100">
{progress.percentage}%
</progress>
)}
<button type="submit">Submit</button>
</form>
);
}
i think its problem with onChange={(e) => setData("avatar", e.target.files[0])}
I had the same issue... I solved removing the value attribute from the input type="file".
<input
type="file"
onChange={(e) => setData("avatar", e.target.files[0])}
/>
(If you see in the documentation the same example for svelte or vue, you will not see the value attribute there).

how to use useRef from outside of the component

what i am trying to achieve that i made a react custom component for an input field looks like this in a seperate folder with name input.js
export function Input({id,type,label,name}) {
return (
<div className="form_container">
<input type={type} id={id} className="form__input" name={name} autoComplete="" placeholder=" " />
<label htmlFor="email" className="form__label">{label}</label>
</div>
)
}
and i used it inside my contact.js file which i want to use multiple times like this
const fname = useRef(null);
<form action="" onSubmit={sendEmail}>
<Input ref={fname} id='inputName' type="text" name="fname" label="Name" />
<button type='submit'> Submit </button>
</form>
const sendEmail = (e) => {
e.preventDefault();
console.log(fname.current.value);
}
but i am getting this error while trying to refresh the page and i cannot access the input to get the value of it
react-dom.development.js:67 Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
only the old class components can be given refs natively.
use need useImperativeHandle hook combined with forwardRef
https://reactjs.org/docs/hooks-reference.html#useimperativehandle

useSelector is unable to fetch latest value of state after re-render and return null

I am building a user page, in which i am getting user info from state itself using useState hook of redux.
import React, { useState, useEffect } from "react";
import profileImage from "../../resources/images/defaultProfile.png";
import { useSelector, useDispatch } from "react-redux";
import { userActions } from "../../state/actions";
const User = () => {
const [isEditable, setIsEditable] = useState(false);
const onChange = (e) => {
setUser({
...user,
[e.target.name]: [e.target.value],
});
};
const onSubmit = () => {
if (!isEditable) {
setIsEditable(!isEditable);
} else {
//TODO update user data here
}
};
const [user, setUser] = useState(useSelector((state) => state.user.user));
return (
user !== null && (
<div className="container-fluid">
<div className="row">
<div className="col-md-9 border border-primary">user preference</div>
<div className="col-md-3 border border-primary">
<div className="row-cols-md-3 text-center">
<img
src={profileImage}
alt="Profile image"
style={{ width: "60%", height: "70%" }}
className="border rounded-circle"
/>
<form onSubmit={onSubmit} style={{ width: "100%" }}>
<div className="mb-3">
<label htmlFor="userName" className="form-label">
User Name
</label>
<input
type="text"
name="name"
id="userName"
value={user.name}
onChange={onChange}
disabled={!isEditable ? "disabled" : ""}
className="form-control"
/>
</div>
<div className="mb-3">
<label htmlFor="userEmail" className="form-label">
User Email
</label>
<input
type="email"
name="email"
disabled={!isEditable ? "disabled" : ""}
id="userEmail"
value={user.email}
onChange={onChange}
className="form-control"
/>
</div>
<div className="mb-3">
<label htmlFor="userPhone" className="form-label">
User Phone Number
</label>
<input
type="text"
disabled={!isEditable ? "disabled" : ""}
name="phoneno"
id="userPhone"
value={user.phoneno}
onChange={onChange}
className="form-control"
/>
</div>
<button type="submit">{isEditable ? "Update" : "Edit"}</button>
</form>
</div>
</div>
</div>
</div>
)
);
};
export default User;
The data is loading fine for the first time. As shown initially all my input field are disabled but once i click on edit, it changes state isEditable and enables every field and change the submit button to "update" .It's re-render the page once the state isEditable is changed. I am loading navbar by default for every page and navBar has code which reloads the value of user state in case of refresh and once the page re-renders it reloads the user global state but somehow useSelector is unable to get latest value and just returns the null value.
Observation -
once the complete re-renders happen useSelector is unable to fetch latest user data although the state has the latest value.
Can somebody please suggest what approach should i choose to overcome the following scenerio.
Never do
useState(useSelector(...));
It does not do what you expect.
useState is initialized once with the argument you put into it - so at first render, it will "capture" the value the selector returns at that point in time. But it will never update the state once the return value from useSelector changes. The local state is initialized after all.
Well i was surfing internet for the following issue and i found this.
https://dmitripavlutin.com/react-forms-tutorial/
" By default, when clicking the form’s Submit button, the browser performs a full-page POST request to the URL specified in the action attribute of the . But having the form controlled by React, you can prevent browser’s default behavior by attaching an event handler to onSubmit event and calling event.preventDefault(). "
And based on this i bring my button out of the form, since now no complete post request would be called and hence it started working.
Frankly i am still not sure, earlier why it was working fine after second click.

In this basic react application, why does the custom component `TextInput` allow me to type stuff in even though the regular `input` field doesn't?

I have a basic react form that I am testing out currently. It's still incomplete, but I have discovered unexpected behaviour.
The regular input field doesn't allow me to type in anything, as I am not updating the state yet. However, my custom TextInput component does allow me to type stuff in... Surprising, as I said before, I am not using setValues to update the state yet.
import React, { useState } from 'react';
const App = () => {
const [values, setValues] = useState({
firstName: '',
lastName: ''
});
return (
<div>
<form>
{/* [EXPECTED] this doesn't allow anything to be typed in at the front-end... which is expected...
... as I am not using `setValues` to update the state yet */}
<input
type="text"
id="first-name"
name="firstName"
value={values.firstName}
/>
{/* [NOT EXPECTED] this does allow stuff to be typed in at the front-end... which is strange...
... as I am not using `setValues` to update the state yet */}
<TextInput
id="last-name"
name="lastName"
value={values.lastName}
/>
<button type="submit">
Register
</button>
</form>
</div>
);
};
const TextInput = props => {
return (
<input
type="text"
id={props.id}
name={props.name}
/>
/* <span id={props.id + '-error'}>{props.title}</span> */
);
};
export default App;
Can anybody help me to explain why the difference?
Your first input is controlled - it has a value prop which is used to determine what the value of the element should be when rendered:
<input
type="text"
id="first-name"
name="firstName"
value={values.firstName}
/>
No matter what you type into it, since it's "controlled", the value that exists in it will always be what's currently in state as values.firstName.
In contrast, your second input is uncontrolled. It has no value prop:
<input
type="text"
id={props.id}
name={props.name}
/>
So, since you're not giving React any directives on what its value should be while being rendered, you can type whatever you want into it, and it won't be in conflict with React state.

How to make react semantic UI TextArea with default value, but still allows user to change the value?

I have tried using defaultValue and value in TextArea but it wont allow user to change the value.
The parent div of the textarea is redux-form Field. And try to pass the value stored in redux state to the textarea
<Field
name="message"
component={renderField}
...
onChange={ (e: any) =>
triggerHoldResponse(e.target.value, conversation.id)
}
/>
const renderField = ({input, type, meta: { error }}: any) => (
<div>
<TextArea
className={styles.textarea}
{...input}
placeholder={placeholder}
type={type}
/>
<div className={styles.errorSignal}>
{error}
</div>
</div>
);
SUI TextArea is base class for Form.TextArea, and both uses the same prop value for setting the default Text Value for the textarea.
Following Code works for me:
import React from 'react'
import { Form, Input, TextArea, Button } from 'semantic-ui-react'
const FormTextAreaExample = () => (
<Form
<Form.TextArea
autoHeight
onChange={this.handleMessageChange}
label="Message"
value={mesg}
placeholder="Enter your request message"
rows={3}
/>
</Form>
)
export default FormTextAreaExample;
Where value={mesg}, sets the default state of textarea (is set).
If you are using Semantic UI react you can use a Form.Field tag
You can set a default value and do what you are asking by using
"defaultValue='default text you are trying to display'
You can see a Form.Field Example below.
<Form>
<Form.Field
name="description"
required control={TextArea}
width={8}
onChange={this.handleChange}
label="Event Description"
defaultValue="Default text..."
type="text"
placeholder="Describe your event!"/>
</Form>
Note: defaultValue will override your placeholder and the defaultValue will not be removed when the click on textarea.
If you want to just display info of what the textarea is for I would use placeholder.
You can link the value of the Textarea input to that of a value from state such that you can set the default text for the text but also allow for the state to be updated in turn updating the value. You can accomplish this using linkstate.
Also if you already have the date in in your store you can set it up on the ComponentDidMount lifecycle event. to set the value of the linked state value therefore setting a default value.
Example usage from the docs:
import linkState from 'linkstate';
class Foo extends Component {
state = {
text: ''
};
render(props, state) {
return (
<input
value={state.text}
onInput={linkState(this, 'text')}
/>
);
}
}
https://github.com/developit/linkstate
An alternative way is to use the textarea tag since I was not able to find proper solution
example:
<label className="wps-label">Message</label>
<textarea
name="message"
placeholder='Your message'
onChange={(e)=> setMessage(e.target.value)}
>
{message}
</textarea>
<Form.Checkbox
name='show_tooltip'
label='Show popup or tooltip when hovering over the spoiler'
value='yes'
onChange={onChange}
defaultChecked={data?.show_tooltip}
/>
or via react-final-form
<Form
initialValues={{
title: modalView === 'add' ? '' : data?.title?.rendered,
content: modalView === 'add' ? '' : data?.content?.rendered
}}
...

Resources