How to display a popup modal in React and getForms.io - reactjs

I am having a problem in displaying a popup when subbmitting a data to getForms. It executes the data but i cannot display the modal. I need help.
`
import axios from'axios'
import {React ,useState} from 'react'
import { useForm} from 'react-hook-form'
import Modal from '../../Modal'
export default function ContactForm(){
const [serverState , setServerState] = useState({submitting: false, status: null})
const handleServerResponse = (ok, msg, form) => {
setServerState({ submitting: false, status: { ok, msg } })
if(ok){
console.log('msg')//THIS WORKS!!
<Modal/>//THIS DOESNT WORK!!
)
return
}
return
}
const { register, handleSubmit, watch, formState: { errors } } = useForm()
const onSubmit = (data,e) =>{
const formData = new FormData()
for(var field in data){
formData.append(field, data[field])
}
setServerState({submitting:true})
axios({
method:'post',
url:'https://getform.io/f/79e16760-4dbb-4e67-b212-c98e3f1b8eda',
data: formData
})
.then((r) => {
e.target.reset();
handleServerResponse(true, "Form Submitted",e)
})
.catch((r) => {
handleServerResponse(false, r.response.data.error, e);
})
}
return(
<section>
<form
onSubmit={handleSubmit(onSubmit)}
className='mt-5 gap-4 text-black flex flex-col'
>
<input className='pl-2 rounded-sm focus:outline-none' type='text' placeholder='Enter yourName' {...register("fullname")}/>
<input className='pl-2 rounded-sm focus:outline-none' type='email' placeholder='Enter your Email' {...register("email")}/>
<textarea className='pl-2 pt-2 rounded-sm focus:outline-none' rows={10} placeholder='Enter your Message'{...register('emailMessage')}/>
<input className=' text-white drop-shadow-lg rounded-lg bg-tertiary p-2 mx-auto' type='submit'/>
</form>
</section>
)
}
`
Im expecting a popup after the data was submitted or not submitted. it do works in console.log but not in React Component.
const [serverState , setServerState] = useState({submitting: false, status: null})
const handleServerResponse = (ok, msg, form) => {
setServerState({ submitting: false, status: { ok, msg } })
if(ok){
//console.log() works!!!!!
//React Component not working!!!!
return
}
//Same HERE!!
return
}

How do you find out that the React code is not working? It is not visible from the code you share. I only see the Modal import, but not its usage. Try logging inside the React component to see if it is rendered and if it is, then try increasing the z-index of the modal, so you can see it.
Ok, with your Modal added to the code I see two errors here, first you don't even return the Modal from this function and second, if you return it, you don't expect it anywhere in your code. Try to render the Modal conditionally in your section instead, like this:
return(
<section>
{showModal && (
<Modal />
)}
<form
onSubmit={handleSubmit(onSubmit)}
className='mt-5 gap-4 text-black flex flex-col'
>
...
</form>
</section>
)
Then add showModal variable into state like this:
const [showModal, setShowModal] = useState(false);
And finally change the handleServerResponce function like this:
if(ok){
console.log('msg')//THIS WORKS!!
setShowModal(true);
)
You will have to add close modal logic into your Modal after that.
And here is how the whole component will look like when you add also logic for passing the message to the modal:
import axios from'axios'
import {React, useState} from 'react'
import { useForm} from 'react-hook-form'
import Modal from '../../Modal'
export default function ContactForm(){
const [serverState , setServerState] = useState({submitting: false, status: null})
const [showModal, setShowModal] = useState(false);
const handleServerResponse = (ok, msg, formData) => {
setServerState({ submitting: false, status: { ok, msg } })
if(ok){
console.log('msg')//THIS WORKS!!
setShowModal(true);
)
return
}
return
}
const { register, handleSubmit, watch, formState: { errors } } = useForm()
const onSubmit = (data,e) =>{
const formData = new FormData()
for(var field in data){
formData.append(field, data[field])
}
setServerState({submitting:true})
axios({
method:'post',
url:'https://getform.io/f/79e16760-4dbb-4e67-b212-c98e3f1b8eda',
data: formData
})
.then((r) => {
e.target.reset();
handleServerResponse(true, "Form Submitted", r);
})
.catch((r) => {
handleServerResponse(false, r.response.data.error, e);
})
}
return(
<section>
{showModal && (
<Modal message={serverState.status ? serverState.status.msg : ""} />
)}
<form
onSubmit={handleSubmit(onSubmit)}
className='mt-5 gap-4 text-black flex flex-col'
>
<input className='pl-2 rounded-sm focus:outline-none' type='text' placeholder='Enter yourName' {...register("fullname")}/>
<input className='pl-2 rounded-sm focus:outline-none' type='email' placeholder='Enter your Email' {...register("email")}/>
<textarea className='pl-2 pt-2 rounded-sm focus:outline-none' rows={10} placeholder='Enter your Message'{...register('emailMessage')}/>
<input className=' text-white drop-shadow-lg rounded-lg bg-tertiary p-2 mx-auto' type='submit'/>
</form>
</section>
)
}

Related

How to connect react-hook-form ref with a custom input component

I'm interested in using react-hook-form for data validation. I have a custom TextField component as follows.
src/components/Fields.js
function Label({ id, children }) {
return (
<label
htmlFor={id}
className="block mb-3 text-sm font-medium text-gray-700"
>
{children}
</label>
);
}
export function TextField({
id,
label,
inputRef,
type = "text",
className = "",
...props
}) {
return (
<div className={className}>
{label && <Label id={id}>{label}</Label>}
<input id={id} ref={inputRef} type={type} {...props} />
</div>
);
}
I've tried using react-hook-form like this..
src/App.js
import { TextField } from "./components/Fields";
import { useForm } from "react-hook-form";
import { useEffect } from "react";
export default function App() {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm();
const mytest = register("mytest", { required: "mytest is a required field" });
const onSubmit = (data) => console.log(data);
useEffect(() => console.log(errors), [errors])
return (
<form onSubmit={handleSubmit(onSubmit)}>
<TextField
id="mytest"
name={mytest.name}
inputRef={mytest.ref}
label="This is a test"
placeholder="Placeholder"
/>
<input type="submit" />
</form>
);
}
but it's not working properly.
I've also tried using forwardRef to no avail.
Finally got this working after applying forwardRef properly. Still curious if there's a better way, so I'll leave this question open.
src/components/Fields.js
import { forwardRef } from "react";
function Label({ id, children }) {
return (
<label
htmlFor={id}
className="block mb-3 text-sm font-medium text-gray-700"
>
{children}
</label>
);
}
export const TextField = forwardRef(function TextField({
id,
label,
type = "text",
className = "",
...props
}, ref) {
return (
<div className={className}>
{label && <Label id={id}>{label}</Label>}
<input id={id} ref={ref} type={type} {...props} />
</div>
);
});
src/App.js
import { TextField } from "./components/Fields";
import { useForm } from "react-hook-form";
import { useEffect } from "react";
export default function App() {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm();
const mytest = register("mytest", { required: "mytest is a required field" });
const onSubmit = (data) => console.log("data", data);
useEffect(() => console.log(errors), [errors])
return (
<form onSubmit={handleSubmit(onSubmit)}>
<TextField
id="mytest"
label="This is a test"
placeholder="Placeholder"
{...mytest}
/>
{errors.mytest && <>{errors.mytest.message}</>}
<input type="submit" />
</form>
);
}

How to pass key:value onChange from select-option in reactjs?

I follow this tutorial and try to modify it a little.
I have this code addRequestForm.js
import { BiPlus } from 'react-icons/bi'
import { useMutation, useQueryClient } from "react-query"
import { addRequest, getRequests } from "../../lib/helper"
import Bug from "./bugRequest"
import Success from "./successRequest"
export default function AddRequestForm({ formData, setFormData }) {
const queryClient = useQueryClient()
const addMutation = useMutation(addRequest, {
onSuccess: () => {
queryClient.prefetchQuery('requests', getRequests)
}
})
const handleSubmit = (e) => {
e.preventDefault();
if (Object.keys(formData).length == 0) return console.log("Don't have Form Data");
let { sector, contact_name } = formData;
const model = {
sector, contact_name
}
addMutation.mutate(model)
}
if (addMutation.isLoading) return <div>Loading!</div>
if (addMutation.isError) return <Bug message={addMutation.error.message}></Bug>
if (addMutation.isSuccess) return <Success message={"Added Successfully"}></Success>
return (
<form className="grid lg:grid-cols-2 w-4/6 gap-4 md:px-4 md:mx-auto" onSubmit={handleSubmit}>
<div className="input-type">
<label htmlFor="sector" className="block text-sm font-medium text-gray-700">
Sector
</label>
<select
id="sector"
name="sector"
autoComplete="sector-name"
className="border w-full px-5 py-3 focus:outline-none rounded-md"
onChange={(e) => setFormData({ [e.target.name]: e.target.value })}
>
<option value="North">North</option>
<option value="South">South</option>
<option value="West">West</option>
<option value="East">East</option>
{/* */}
</select>
</div>
<div className="form-control input-type">
<label className="input-group">
<span>Contact Name</span>
<input type="text" onChange={setFormData} name="contact_name" placeholder="Contact Name Here..“ className="w-full input input-bordered" />
</label>
</div>
<button type="submit" className="flex justify-center text-md w-2/6 bg-green-500 text-white px-4 py-2 border rounded-md hover:bg-gray-50 hover:border-green-500 hover:text-green-500">
Add <span className="px-1"><BiPlus size={24}></BiPlus></span>
</button>
</form >
)
}
For name=“contact_name” already succeed insert into database (mongodb + mongoose). But, I’m still confuse, how to do the same to select-option?
Already tried these:
onChange={(e) => setFormData({ e.target.name: e.target.value })}
onChange={(e) => setFormData({ sector.name: e.target.value })}
onChange={(e) => setFormData({ [e.target.name][0]: e.target.value })}
Got Syntax error: Unexpected token, expected ",". While I thought this is the right value when I’m consoling these ( result: “sector:myname”).
onChange={(e) => setFormData({ sector: e.target.value })}
onChange={(e) => setFormData({ [e.target.name]: e.target.value })}
No insert into database.
How to write it correctly? I’m using nextjs (i thought the same with reactjs, right?) + #reduxjs/toolkit 1.9.1.
In case needed, here is reducer.js:
import UpdateRequestForm from "./updateRequestForm";
import AddRequestForm from "./addRequestForm";
import { useSelector } from "react-redux";
import { useReducer } from "react";
const formReducer = (state, event) => {
return {
...state,
[event.target?.name]: event.target?.value
}
}
export default function FormRequest() {
const [formData, setFormData] = useReducer(formReducer, {})
const formId = useSelector((state) => state.app.client.formId)
return (
<div className="container mx-auto py-5">
{formId ? UpdateRequestForm({ formId, formData, setFormData }) : AddRequestForm({ formData, setFormData })}
</div>
)
}
try this
onChange = {(e) => {
let obj = {} ;
obj[e.target.name] = e.target.value;
setFormData(obj);
}
}
I'm not entirely following your use case for the reducer but you could replace it with a simple handleChange function as follows. Just a side note, calling setFormData(//newData) will complete erase what is in there so we first spread the original state and change the value based on the key.
State and handleChange:
const [formData, setFormData] = React.useState({
sector: "North",
contact_name: ""
});
const handleChange = ({ target }) => {
// Deconstruct the target
// Deconstruct the name and value from the target
const { name, value } = target;
// Set the state
setFormData((previousState) => ({
...previousState,
[name]: value,
}));
};
Usage:
<select
placeholder="Select a sector"
name="sector"
value={formData.sector}
onChange={handleChange}
>
<option value="North">North</option>
<option value="South">South</option>
<option value="West">West</option>
<option value="East">East</option>
</select>
<input
value={formData.contact_name}
placeholder="Contact Name"
name="contact_name"
onChange={handleChange}
/>
A codesandbox : https://codesandbox.io/s/interesting-kilby-emcyl4?file=/src/App.js:461-615
Also note that you will need to assign a name to the input element. In the example, name is set to sector. As the name value will be used to indicate which key in the state object must be updated.
It's good practice to have controlled input fields. So setting the value of the input to your state will accomplish this. Only thing to remember is you need a default state. I have added it all to the codesandbox if you would like to take a look.
In my case, just use this syntax:
…
value={formData.sector}
onChange={setFormData}
…
Case closed.

How to post a csv file using useForm from 'react-hook-form?

I got a query using RTK, and a component where I attach the file and submit - after I attach the file and submit as shown in the image below I get the file in a weird format such as: file: "[object FileList]" after console log it.
How was wondering how to post a csv file in the correct format, such as file.csv. My component looks like as follow:
import { FC, FormEventHandler, RefObject, useState } from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '#hookform/resolvers/yup';
import * as yup from 'yup';
import {
Dialog,
DialogPortal,
DialogOverlay,
DialogContent,
DialogTitle,
DialogClose
} from '#radix-ui/react-dialog';
import { DownloadIcon, XIcon } from '#heroicons/react/outline';
import { setFormErrors } from 'utils/formHelpers';
import InputField from 'components/ui/atoms/InputField';
import { useUploadBatchMutation } from 'redux/api/uploadBatchApi';
const schema = yup
.object({
description: yup.string().optional(),
file: yup.string().label('file').required()
})
.required();
interface Inputs extends yup.InferType<typeof schema> {
base: null;
}
interface Props {
open: boolean;
onClose: (value: boolean) => void;
triggerRef: RefObject<HTMLAnchorElement>;
}
const UploadBatchDialog: FC<Props> = ({ open, onClose, triggerRef }) => {
const [showLoading, setShowLoading] = useState<boolean>(false);
const {
handleSubmit,
register,
setError,
formState,
setValue,
reset: resetForm
} = useForm<Inputs>({
mode: 'onSubmit',
resolver: yupResolver(schema)
});
const [uploadBatch] = useUploadBatchMutation();
const onSubmit: FormEventHandler<HTMLFormElement> = async (event) => {
console.log('test');
event.preventDefault();
try {
await handleSubmit(async (data) => {
console.log(data);
const formData = new FormData();
formData.append('file', data.file);
if (data.description) {
formData.append('name', data.description);
}
uploadBatch(formData);
})(event);
} catch (error: any) {
setTimeout(
() =>
setFormErrors({
error,
setError,
fields: Object.keys(schema.fields)
}),
50
);
}
};
const formFailed = () => Object.keys(formState.errors).length > 0;
const onDialogClose = (value: boolean) => {
resetForm();
onClose(value);
};
const onCloseAutoFocus = () => triggerRef.current?.focus();
const [file, setFile] = useState<any>();
return (
<Dialog open={open} onOpenChange={onDialogClose}>
<DialogPortal>
<DialogOverlay className="bg-opacity/90 fixed inset-0 z-50 bg-inpay-green-700">
<DialogContent
onCloseAutoFocus={onCloseAutoFocus}
onInteractOutside={(e) => e.preventDefault()}
className="fixed top-[50%] left-[50%] translate-y-[-50%] translate-x-[-50%] rounded-lg shadow-[0_0_6px_#B7BCBA]"
>
<DialogTitle className="flex h-12 items-center justify-between rounded-t-lg border-b border-b-inpay-pumice bg-white px-6">
<DownloadIcon className="w-8 stroke-1 group-hover:stroke-1.5" />
<span className="pr-20">Upload batch file</span>
<DialogClose
aria-label="Close"
className="outline-none focus:outline-none"
>
<XIcon className="-m-1 h-8 w-8 outline-none focus:outline-none" />
</DialogClose>
</DialogTitle>
<div className="flex justify-center rounded-b-lg bg-inpay-black-haze-100 p-6">
<div className="w-72">
<form onSubmit={onSubmit} autoComplete="off">
<InputField
{...register('description')}
className="mb-4"
label="Name (if left blank the file name will be used)"
name="description"
/>
<div className="mb-4 flex flex-col">
<label className="mb-0.5 pl-2 text-xs">
Attach payout request batch file
</label>
<div className="relative flex h-12 w-full rounded-lg bg-white p-1 shadow-plain focus:shadow-plain-lg group-hover:shadow-plain-lg">
<input
className="absolute opacity-0"
type="file"
accept="text/csv"
{...register('file')}
onChange={(e) => {
if (e.target.files) {
setFile(e.target.files[0]);
} else {
setFile(null);
}
}}
/>
<span className="flex w-full items-center justify-between pl-2">
{file ? file.name : 'Please attach your file'}
<button className="inpay-button h-10">
Attach file
</button>
</span>
</div>
{/* <div>Create batch template</div> */}
</div>
<button
type="submit"
className="inpay-button w-full"
// disabled={
// formState.isSubmitSuccessful || !formState.isValid
// }
>
Upload batch
</button>
{formState.errors.base && (
<div
role="alert"
className="mt-2 rounded-2xl bg-rose-200 p-3 text-sm text-inpay-red-600"
style={{
filter: 'drop-shadow(0 0 1px rgb(0 0 0 / 0.16))'
}}
>
{formState.errors.base.message}
</div>
)}
</form>
</div>
</div>
</DialogContent>
</DialogOverlay>
</DialogPortal>
</Dialog>
);
};
export default UploadBatchDialog;
while my post request looks like this:
import { customerApi } from './customerApi';
interface UploadedFile {
file: any;
name: string;
}
export const uploadBatchApi = customerApi.injectEndpoints({
endpoints: (builder) => ({
uploadBatch: builder.mutation<any, any>({
query(form) {
return {
url: 'payouts/payout_request_csv_batches',
method: 'POST',
credentials: 'include',
body: form
};
}
})
})
});
export const { useUploadBatchMutation } = uploadBatchApi;
I am wondering what I did wrong and why react hook passes that strange string. Any clues would be much appreciated.

Download URL from storage not transferring to firestore - React [duplicate]

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Why does calling react setState method not mutate the state immediately?
(9 answers)
Closed 7 months ago.
I am trying to add the imgUrl uploaded to firestore but it's not working. The url is generated and the image is stored in firebase storage but I can't seem to pass that URL as a field into Firestore
This is my code
import React, {useEffect, useState} from "react";
import ReactTagInput from "#pathofdev/react-tag-input";
import "#pathofdev/react-tag-input/build/index.css";
import {db, storage} from "../firebase";
import { getDownloadURL, ref, uploadBytes, listAll} from "firebase/storage";
import {
addDoc,
collection,
getDoc,
serverTimestamp,
doc,
updateDoc,
} from "firebase/firestore";
import { toast } from "react-toastify";
import { v4 } from 'uuid';
import {useNavigate} from "react-router-dom";
const initialState = { // initial state for our form
title: "",
tags: [],
trending: "no",
category: "",
description: "",
imgUrl: ""
}
const categoryOption = [
"Fashion",
"Technology",
"Food",
"Politics",
"Sports",
"Business"
]
export default function AddEditBlog({ user }) {
let urlDownload
const [form, setForm] = useState(initialState)
const [imageUpload, setImageUpload] = useState()
const [imageList, setImageList] = useState([])
const navigate = useNavigate()
const imageListRef = ref(storage, "images/") // to use in useEffect (listAll), because we
// wanna access all the files inside the images folder
const { title, tags, category, trending, description, imgUrl} = form;
const uploadImage = () => {
if(imageUpload == null) return; // if there's no image, return and get out of this function
uploadBytes(imageRef, imageUpload).then((snapshot)=> {
getDownloadURL(snapshot.ref).then((url) => {
// setImageList((prev) => [...prev, url])
console.log("url", url)
toast.info("Image upload to firebase successfully");
setForm((prev) => ({ ...prev, imgUrl: url }))
console.log("form", form)
})
})
}
const handleChange = (event) => {
setForm({...form, [event.target.name]: event.target.value})
}
const handleTags = (tags) => {
setForm({ ...form, tags})
}
const handleTrending = (event) => {
setForm({ ...form, trending: event.target.value })
}
const onCategoryChange = (event) => {
setForm({ ...form, category: event.target.value })
}
const handleSubmit = async (event) => {
event.preventDefault()
if (category && tags && title && description && trending && imgUrl) {
try {
console.log("url", urlDownload)
await addDoc(collection(db, "blogs"), {
...form,
timestamp: serverTimestamp(),
author: user.displayName,
userId: user.uid
})
toast.success("Blog created successfully");
} catch(error) {
console.log(error)
}
} else {
console.log("complete form")
}
navigate("/")
}
return(
<div className="container-fluid mb-4">
<div className="container">
<div className="col-12">
<div className="text-center heading py-2">
Create Blog
</div>
</div>
<div className="row h-100 justify-content-center align-items-center">
<div className="col-10 col-md-8 col-lg-6">
<form className="row blog-form" onSubmit={handleSubmit}>
<div className="col-12 py-3">
<input
type="text"
className="form-control input-text-box"
placeholder="Title"
name="title"
value={title}
onChange={handleChange}
/>
</div>
<div className="col-12 py-3">
<ReactTagInput
tags={tags}
placeholder="Tags"
onChange={handleTags}
/>
</div>
<div className="col-12 py-3">
<p className="trending">Is it trending blog ?</p>
<div className="form-check-inline mx-2">
<input
type="radio"
className="form-check-input"
value="yes"
name="radioOption"
checked={trending === "yes"} //if trending is yes select checked button
onChange={handleTrending}
/>
<label htmlFor="radioOption" className="form-check-label">
Yes
</label>
<input
type="radio"
className="form-check-input"
value="no"
name="radioOption"
checked={trending === "no"} //if trending is not select checked button
onChange={handleTrending}
/>
<label htmlFor="radioOption" className="form-check-label">
No
</label>
</div>
</div>
<div className="col-12 py-3">
<select
value={category}
onChange={onCategoryChange}
className="catg-dropdown"
>
<option>Please select category</option>
{categoryOption.map((option, index) => (
<option value={option || ""} key={index}>
{option}
</option>
))}
</select>
</div>
<div className="col-12 py-3">
<textarea
className="form-control description-box"
placeholder="Description"
value={description}
name="description"
onChange={handleChange}
/>
</div>
<div className="mb-3">
<input
type="file"
className="form-control"
onChange={(event)=> {
setImageUpload(event.target.files[0])
}}
/>
</div>
<div className="col-12 py-3 text-center">
<button
className="btn btn-add"
type="submit"
onClick={uploadImage}
>
Submit
</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
Everything else loads successfully. The console log I do of the url returns the correct URL so I don't know why I can't pass it into firestore

React Apollo: How to access element Trix-Editor within a Mutation component?

Please, I need help accessing a <trix-editor> element in my Mutation component. I'm using React hook useRef() to access this, but getting null. From Chrome Dev Tools (image below) I can see the element has been referred. I have tried some of the solutions given here & here but with no luck. Problem seems to be in the rendering of the Mutation component. I'm fairly new to React, so not sure if I'm getting this right. What am I doing wrong? Thank you in advance for your help.
My code:
const EditProfile = () => {
const trixInput = useRef(null);
const [state, setState] = useState({
firstName: "",
lastName: "",
bio: "",
avatar: "",
gender: "",
country: ""
});
let userID
let { username } = useParams();
let userFromStore = JSON.parse(sessionStorage.getItem('_ev')));
let history = useHistory();
useEffect(() => {
// trixInput.current.addEventListener("trix-change", event => {
console.log(trixInput.current); // <<<< this prints null to the screen
// })
},[]);
if (userFromStore !== username) {
return (
<div className="alert">
<span className="closebtn" onClick={() => history.push("/console")}>×</span>
<strong>Wanning!</strong> Not authorized to access this page.
</div>
);
}
return (
<Query query={GetAuthUser}>
{({ loading, error, data }) => {
if (loading) return "loading...";
if (error) return `Error: ${error}`;
if (data) {
let {userId, firstName, lastName, avatar, bio, gender, country} = data.authUser.profile;
userID = parseInt(userId);
return (
<Mutation mutation={UpdateUserProfile}>
{(editProfile, { loading }) => (
<div className="card bg-light col-md-8 offset-md-2 shadow p-3 mb-5 rounded">
<article className="card-body">
<form onSubmit={ e => {
e.preventDefault();
editProfile({
variables: {
userId: userID,
firstName: state.firstName,
lastName: state.lastName,
bio: state.bio,
avatar: state.avatar,
gender: state.gender,
country: state.country
}
}).then(({ data: { editProfile: { success, errors } } }) => {
success ? alert(success) : alert(errors[0]["message"]);
});
}}
>
<div className="form-row mb-3">
<div className="col">
<input
type="text"
name="firstName"
placeholder="First name"
className="form-control"
defaultValue={firstName}
onChange={e => setState({firstName: e.currentTarget.value})}
/>
</div>
<div className="col">
<input
type="text"
name="lastName"
placeholder="Last name"
className="form-control"
defaultValue={lastName}
onChange={e => setState({lastName: e.currentTarget.value})}
/>
</div>
</div>
<div className="form-group">
<label className="">Bio</label>
<input
type="hidden"
defaultValue={bio}
name="bio"
id="bio-body"
/>
// <<< having trouble accessing this ref element
<trix-editor input="bio-body" ref={trixInput}/>
</div>
<input type="submit" className="btn btn-primary" value="Update Profile" disabled={loading}/>
</form>
</article>
</div>
)}
</Mutation>
);
}
}}
</Query>
)
}
export default EditProfile;
UPDATE:
For anyone interested, problem was solved by extracting Mutation component to a different file. Reason been Mutation component wasn't mounting on render, only the Query component was mounting. First iteration of the solution is shown as an answer.
EditProfile component
import React, { useState } from 'react';
import { Query } from 'react-apollo';
import { useHistory, useParams } from "react-router-dom";
import { GetAuthUser } from './operations.graphql';
import ProfileMutation from './ProfileMutation'
const EditProfile = (props) => {
const [state, setState] = useState({
firstName: "",
lastName: "",
bio: "",
avatar: "",
gender: "",
country: ""
});
let { username } = useParams();
let userFromStore = JSON.parse(sessionStorage.getItem('_ev'));
let history = useHistory();
if (userFromStore !== username) {
return (
<div className="alert">
<span className="closebtn" onClick={() => history.push("/console")}>×</span>
<strong>Wanning!</strong> Not authorized to access this page.
</div>
);
}
return (
<Query query={GetAuthUser}>
{({ loading, error, data }) => {
if (loading) return "loading...";
if (error) return `Error: ${error}`;
return <ProfileMutation state={state} profile={data.authUser.profile} setState={setState}/>
}}
</Query>
)
}
export default EditProfile;
ProfileMutation component
import React, { useRef, useEffect } from 'react';
import { Mutation } from 'react-apollo';
import { UpdateUserProfile } from './operations.graphql';
import "trix/dist/trix.css";
import "./styles.css"
const ProfileMutation = ({ state, profile, setState }) => {
const trixInput = useRef('');
let { userId, firstName, lastName, avatar, bio, gender, country } = profile;
let userID = parseInt(userId);
// console.log(firstName)
useEffect(() => {
trixInput.current.addEventListener('trix-change', (e) => {
setState({bio: trixInput.current.value})
console.log(trixInput.current.value)
})
},[trixInput])
return (
<Mutation mutation={UpdateUserProfile}>
{(editProfile, { loading }) => (
<div className="card bg-light col-md-8 offset-md-2 shadow p-3 mb-5 rounded">
<article className="card-body">
<form onSubmit={ e => {
e.preventDefault();
editProfile({
variables: {
userId: userID,
firstName: state.firstName,
lastName: state.lastName,
bio: state.bio,
avatar: state.avatar,
gender: state.gender,
country: state.country
}
}).then(({ data: { editProfile: { success, errors } } }) => {
success ? alert(success) : alert(errors[0]["message"]);
//TODO: replace alerts with custom message box
});
}}
>
<div className="form-row mb-3">
<div className="col">
<input
type="text"
name="firstName"
placeholder="First name"
className="form-control"
defaultValue={firstName}
onChange={e => setState({firstName: e.currentTarget.value})}
/>
</div>
<div className="col">
<input
type="text"
name="lastName"
placeholder="Last name"
className="form-control"
defaultValue={lastName}
onChange={e => setState({lastName: e.currentTarget.value})}
/>
</div>
</div>
<div className="form-group">
<label className="">Bio</label>
<input
type="hidden"
defaultValue={bio}
name="bio"
id="bio"
/>
<trix-editor input="bio" ref={trixInput} />
</div>
<input type="submit" className="btn btn-primary" value="Update Profile" disabled={loading}/>
</form>
</article>
</div>
)}
</Mutation>
);
}
export default ProfileMutation;
Hope this helps someone! If anyone has a better solution please post it here. Thanks!

Resources