why am i getting undefined for match object in react.js - reactjs

The below code is to update a product in admin panel. I have manage to do everything except that on a button pressed for update i want to fetch the url from the above address bar. I thought of using match.params.productId. Here produtId is what i wanted ti get. But i dont know why the match object returns undefined..PLease help me to get rid of this.
import React, { useState, useEffect } from "react";
import Base from "../core/Base";
import { Link } from "react-router-dom";
import {
getAllCategories,
getProduct,
updateProduct
} from "./helper/adminapicall";
import { isAuthenticated } from "../auth/helper/index";
const UpdateProduct = ({ match }) => {
const { user, token } = isAuthenticated();
const [values, setValues] = useState({
name: "",
description: "",
price: "",
stock: "",
photo: "",
categories: [],
category: "",
loading: false,
error: "",
createdProduct: "",
getaRedirect: false,
formData: ""
});
const {
name,
description,
price,
stock,
categories,
category,
loading,
error,
createdProduct,
getaRedirect,
formData
} = values;
const preload = (productId) => {
getProduct(productId).then((data) => {
console.log(data);
if (data.error) {
setValues({ ...values, error: data.error });
} else {
preloadCategories();
setValues({
...values,
name: data.name,
description: data.description,
price: data.price,
category: data.category._id,
stock: data.stock,
formData: new FormData()
});
}
});
};
const preloadCategories = () => {
getAllCategories().then((data) => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({
categories: data,
formData: new FormData()
});
}
});
};
useEffect(() => {
//console.log(match);
preload(match.params.productId);
}, []);
//TODO: work on it
const onSubmit = (event) => {
event.preventDefault();
setValues({ ...values, error: "", loading: true });
updateProduct(match.params.productId, user._id, token, formData).then(
(data) => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({
...values,
name: "",
description: "",
price: "",
photo: "",
stock: "",
loading: false,
createdProduct: data.name
});
}
}
);
};
const handleChange = (name) => (event) => {
const value = name === "photo" ? event.target.files[0] : event.target.value;
formData.set(name, value);
setValues({ ...values, [name]: value });
};
const successMessage = () => (
<div
className="alert alert-success mt-3"
style={{ display: createdProduct ? "" : "none" }}
>
<h4>{createdProduct} updated successfully</h4>
</div>
);
const createProductForm = () => (
<form>
<span>Post photo</span>
<div className="form-group">
<label className="btn btn-block btn-success">
<input
onChange={handleChange("photo")}
type="file"
name="photo"
accept="image"
placeholder="choose a file"
/>
</label>
</div>
<div className="form-group">
<input
onChange={handleChange("name")}
name="photo"
className="form-control"
placeholder="Name"
value={name}
/>
</div>
<div className="form-group">
<textarea
onChange={handleChange("description")}
name="photo"
className="form-control"
placeholder="Description"
value={description}
/>
</div>
<div className="form-group">
<input
onChange={handleChange("price")}
type="number"
className="form-control"
placeholder="Price"
value={price}
/>
</div>
<div className="form-group">
<select
onChange={handleChange("category")}
className="form-control"
placeholder="Category"
>
<option>Select</option>
{categories &&
categories.map((cate, index) => (
<option key={index} value={cate._id}>
{cate.name}
</option>
))}
</select>
</div>
<div className="form-group">
<input
onChange={handleChange("stock")}
type="number"
className="form-control"
placeholder="Stock"
value={stock}
/>
</div>
<button
type="submit"
onClick={onSubmit}
className="btn btn-outline-success mb-3"
>
Update Product
</button>
</form>
);
return (
<Base
title="Add a product here!"
description="Welcome to product creation section"
className="container bg-info p-4"
>
<Link to="/admin/dashboard" className="btn btn-md btn-dark mb-3">
Admin Home
</Link>
<div className="row bg-dark text-white rounded">
<div className="col-md-8 offset-md-2">
{successMessage()}
{createProductForm()}
</div>
</div>
</Base>
);
};
export default UpdateProduct;

Related

How to upload image in react

I am working on a e-commerce app and there I am using form to add new product in app for that I am taking multiple inputs from user and then I am storing those inputs in my redux store and finally I am displaying newly added product in my app and everything is working perfectly here earlier I was using <input type="url"/> so user can paste an image url so I can display it and it was working perfectly but now I want to allow user so he can upload an Image from his local system instead or url using <input type="file"/> but this isn,t working this is not loading image from local system so is there any way I can upload Image from local system:
Form
import "./ProductForm.css";
import { useReducer } from "react";
import { useDispatch } from "react-redux";
import { addProductHandler } from "../../store/DataStore";
import { useNavigate } from "react-router-dom";
const ProductForm = () => {
const Dispatch = useDispatch();
const navigate = useNavigate();
const initialState = {
product_id: "",
product_name: "",
product_quantity: "",
product_description: "",
product_type: "",
product_valid: "false",
product_price: "",
product_title: "",
product_image: "",
};
const reducer = (state, action) => {
if (action.type === "id") {
return { ...state, product_id: action.value };
} else if (action.type === "name") {
return { ...state, product_name: action.value };
} else if (action.type === "title") {
return { ...state, product_title: action.value };
} else if (action.type === "price") {
return { ...state, product_price: action.value };
} else if (action.type === "image") {
return { ...state, product_image: action.value };
} else if (action.type === "description") {
return { ...state, product_discription: action.value };
} else if (action.type === "type") {
return { ...state, product_type: action.value };
}
};
const [state, dispatch] = useReducer(reducer, initialState);
const submitHandler = (e) => {
e.preventDefault();
const obj = {
product_id: state.product_id,
product_name: state.product_name,
product_price: +state.product_price,
product_title: state.product_title,
product_image: state.product_image,
product_type: state.product_type,
product_description: state.product_discription,
product_quantity: 0,
product_valid: "false",
};
console.log(obj);
// Dispatch(addProductHandler(obj));
// navigate(`/product`, { replace: true });
};
return (
<form onSubmit={submitHandler}>
<legend>
<center>
<h2>
<b>Add Product</b>
</h2>
</center>
</legend>
<div className="form-group">
<label>Product id</label>
<input
type="text"
className="form-control"
value={state.product_id}
onChange={(e) => dispatch({ type: "id", value: e.target.value })}
/>
</div>
<div className="form-group">
<label>Product Name</label>
<input
type="text"
className="form-control"
value={state.product_name}
onChange={(e) => dispatch({ type: "name", value: e.target.value })}
/>
</div>
<div className="form-group">
<label>Product Price</label>
<input
type="text"
className="form-control"
value={state.product_price}
onChange={(e) => dispatch({ type: "price", value: e.target.value })}
/>
</div>
<div className="form-group">
<label>Product Title</label>
<input
type="text"
className="form-control"
value={state.product_title}
onChange={(e) => dispatch({ type: "title", value: e.target.value })}
/>
</div>
<div className="form-group">
<label>Product Type</label>
<select
className="form-control"
value={state.product_type}
onChange={(e) => dispatch({ type: "type", value: e.target.value })}
>
<option value="" selected disabled>
Select one
</option>
<option value="c1">Commodityies</option>
<option value="g1">Gadgets</option>
<option value="w1">Wearings</option>
</select>
</div>
<div className="form-group">
<label>Product Image</label>
<input
type="file"
className="form-control"
value={state.product_image}
onChange={(e) => dispatch({ type: "image", value: e.target.value })}
/>
</div>
<div className="form-group">
<label>Product Description</label>
<textarea
className="form-control"
id=""
rows="7"
value={state.product_discription}
onChange={(e) =>
dispatch({ type: "description", value: e.target.value })
}
></textarea>
</div>
<button type="submit" className="btn btn-primary mt-4">
Submit
</button>
</form>
);
};
export default ProductForm;
you cannot upload file like that you should have to use FormData Class For multipart/form-data
var formData = new FormData();
formData.append('product_id', state.product_id,)
formData.append('product_name', state.product_name,)
formData.append('product_price', +state.product_price,)
formData.append('product_title', state.product_title,)
formData.append('product_image', state.product_image,)
formData.append('product_type', state.product_type,)
formData.append('product_description', state.product_discription,)
formData.append('product_quantity', 0,)
formData.append('product_valid', "false")
formData.append('image', $('input[type=file]')[0].files[0]);
$.ajax({
url: 'Your url here',
data: formData,
type: 'POST',
contentType: false, // NEEDED, DON'T OMIT THIS (requires jQuery 1.6+)
processData: false, // NEEDED, DON'T OMIT THIS
// ... Other options like success and etc
});
After this it will send ajax request like you submit regular form
with enctype="multipart/form-data"

I cannot understand how setValue is working in init() which used in useEffect?

I know that useEffect( without any dependencies) only work on Mount So how state updates by setValues in init() here??
I am beginner in mern stack so please if you wnat any info please comment
//Code snippet
const init = () => {
getCategories().then(data => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({
...values,
categories: data,
formData: new FormData()
});
}
});
};
useEffect(() => {
init();
}, []);
//full code for help
import React, { useState, useEffect } from 'react';
import Layout from '../core/Layout';
import { isAuthenticated } from '../auth';
import { Link } from 'react-router-dom';
import { createProduct, getCategories } from './apiAdmin';
const AddProduct = () => {
const [values, setValues] = useState({
name: '',
description: '',
price: '',
categories: [],
category: '',
shipping: '',
quantity: '',
photo: '',
loading: false,
error: '',
createdProduct: '',
redirectToProfile: false,
formData: ''
});
const { user, token } = isAuthenticated();
const {
name,
description,
price,
categories,
category,
shipping,
quantity,
loading,
error,
createdProduct,
redirectToProfile,
formData
} = values;
// load categories and set form data
const init = () => {
getCategories().then(data => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({
...values,
categories: data,
formData: new FormData()
});
}
});
};
useEffect(() => {
init();
}, []);
const handleChange = name => event => {
const value = name === 'photo' ? event.target.files[0] : event.target.value;
formData.set(name, value);
setValues({ ...values, [name]: value });
};
const clickSubmit = event => {
event.preventDefault();
setValues({ ...values, error: '', loading: true });
createProduct(user._id, token, formData).then(data => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({
...values,
name: '',
description: '',
photo: '',
price: '',
quantity: '',
loading: false,
createdProduct: data.name
});
}
});
};
const newPostForm = () => (
<form className="mb-3" onSubmit={clickSubmit}>
<h4>Post Photo</h4>
<div className="form-group">
<label className="btn btn-secondary">
<input onChange={handleChange('photo')} type="file" name="photo" accept="image/*" />
</label>
</div>
<div className="form-group">
<label className="text-muted">Name</label>
<input onChange={handleChange('name')} type="text" className="form-control" value={name} />
</div>
<div className="form-group">
<label className="text-muted">Description</label>
<textarea onChange={handleChange('description')} className="form-control" value={description} />
</div>
<div className="form-group">
<label className="text-muted">Price</label>
<input onChange={handleChange('price')} type="number" className="form-control" value={price} />
</div>
<div className="form-group">
<label className="text-muted">Category</label>
<select onChange={handleChange('category')} className="form-control">
<option>Please select</option>
{categories &&
categories.map((c, i) => (
<option key={i} value={c._id}>
{c.name}
</option>
))}
</select>
</div>
<div className="form-group">
<label className="text-muted">Shipping</label>
<select onChange={handleChange('shipping')} className="form-control">
<option>Please select</option>
<option value="0">No</option>
<option value="1">Yes</option>
</select>
</div>
<div className="form-group">
<label className="text-muted">Quantity</label>
<input onChange={handleChange('quantity')} type="number" className="form-control" value={quantity} />
</div>
<button className="btn btn-outline-primary">Create Product</button>
</form>
);
const showError = () => (
<div className="alert alert-danger" style={{ display: error ? '' : 'none' }}>
{error}
</div>
);
const showSuccess = () => (
<div className="alert alert-info" style={{ display: createdProduct ? '' : 'none' }}>
<h2>{`${createdProduct}`} is created!</h2>
</div>
);
const showLoading = () =>
loading && (
<div className="alert alert-success">
<h2>Loading...</h2>
</div>
);
return (
<Layout title="Add a new product" description={`G'day ${user.name}, ready to add a new product?`}>
<div className="row">
<div className="col-md-8 offset-md-2">
{showLoading()}
{showSuccess()}
{showError()}
{newPostForm()}
</div>
</div>
</Layout>
);
};
export default AddProduct
You're right that the useEffect without any dependencies will run once on mount. So that means that the function init() is gonna be called at least once on mount, right? So whatever action you have in your init function will be called once. You don't need dependencies in order to set a state.

Unit testing a registration component with enzyme and jest

I am trying to unit test my registration component
const Register = () => {
const dispatch = useDispatch();
const currentUser = useSelector((state: RootState) => state.user.currentUser);
const errors = useSelector((state: RootState) => state.user.error);
const loading = useSelector((state: RootState) => state.user.loading);
const history = useHistory();
const [email, setEmail] = useState<UseState>({ text: "", touched: false });
const [firstName, setFirstName] = useState<UseState>({
text: "",
touched: false,
});
const [lastName, setLastName] = useState<UseState>({
text: "",
touched: false,
});
const [password, setPassword] = useState<UseState>({
text: "",
touched: false,
isValid: null,
});
const [confirmPassword, setConfirmPassword] = useState<UseState>({
text: "",
touched: false,
isValid: null,
});
const [error, setError] = useState<string>("");
useEffect(() => {
//cleanup
return () => {
dispatch(clearError());
};
//eslint-disable-next-line
}, []);
useEffect(() => {
if (currentUser && currentUser.jwt) {
setEmail({ text: "", touched: false });
setFirstName({ text: "", touched: false });
setLastName({ text: "", touched: false });
setPassword({ text: "", touched: false });
setConfirmPassword({ text: "", touched: false });
}
// eslint-disable-next-line
}, [currentUser]);
const handleRegister = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (
password.text === confirmPassword.text &&
password.isValid &&
confirmPassword.isValid
) {
dispatch(
signUpStart({
email: email.text,
firstName: firstName.text,
lastName: lastName.text,
password: password.text,
confirmPassword: confirmPassword.text,
})
);
} else {
if (errors) {
// cleanup
return () => {
dispatch(clearError());
};
}
setError("Different passwords");
}
};
return (
<section className={styles.register__page}>
{loading && <Loading />}
<article className={styles.register__wrapper}>
<div className={styles.register__wrapper__header}>
<h1>Sign-up</h1>
<p>
Text
</p>
</div>
<div className={styles.register__wrapper__body}>
<form
onSubmit={(e) => handleRegister(e)}
className={styles.register__form}
>
<div className={styles.input__wrapper}>
<FontAwesomeIcon icon={faUser} />
<div className={styles.input__wrapper__inner}>
<input
required
type="email"
name="email"
id="email"
value={email.text}
onChange={(e) =>
setEmail({ text: e.target.value, touched: true })
}
/>
<label
htmlFor="email"
placeholder="E-mail"
className={email.text !== "" ? styles.filled : undefined}
>
E-mail
</label>
</div>
</div>
<div className={styles.input__wrapper}>
<FontAwesomeIcon icon={faUser} />
<div className={styles.input__wrapper__inner}>
<input
type="text"
name="firstname"
id="firstname"
value={firstName.text}
onChange={(e) =>
setFirstName({ text: e.target.value, touched: true })
}
/>
<label
htmlFor="firstname"
className={firstName.text !== "" ? styles.filled : undefined}
>
Name
</label>
</div>
</div>
<div className={styles.input__wrapper}>
<FontAwesomeIcon icon={faUser} />
<div className={styles.input__wrapper__inner}>
<input
type="text"
name="lastname"
id="lastname"
value={lastName.text}
onChange={(e) =>
setLastName({ text: e.target.value, touched: true })
}
/>
<label
htmlFor="lastname"
className={lastName.text !== "" ? styles.filled : undefined}
>
Surname
</label>
</div>
</div>
<div
className={[
styles.input__wrapper,
!password.isValid && password.touched
? styles.wrong
: styles.input__wrapper,
].join(" ")}
>
<FontAwesomeIcon icon={faKey} />
{!password.isValid && password.touched && (
<FontAwesomeIcon
icon={faExclamationCircle}
className={styles.errorIcon}
/>
)}
<div className={styles.input__wrapper__inner}>
<input
required
type="password"
name="password"
id="password"
value={password.text}
onChange={(e) => validateOnChange(e)}
/>
<label
htmlFor="password"
className={password.text !== "" ? styles.filled : undefined}
>
Hasło
</label>
</div>
</div>
<div
className={[
styles.input__wrapper,
!confirmPassword.isValid && confirmPassword.touched
? styles.wrong
: styles.input__wrapper,
].join(" ")}
>
<FontAwesomeIcon icon={faKey} />
{!confirmPassword.isValid && confirmPassword.touched && (
<FontAwesomeIcon
icon={faExclamationCircle}
className={styles.errorIcon}
/>
)}
<div className={styles.input__wrapper__inner}>
<input
required
type="password"
name="confirmPassword"
id="confirmPassword"
value={confirmPassword.text}
onChange={(e) => validateOnChange(e)}
/>
<label
htmlFor="confirmPassword"
className={
confirmPassword.text !== "" ? styles.filled : undefined
}
>
Confirm password
</label>
</div>
</div>
<button type="submit" className={styles.register__btn}>
Rejestruj
</button>
{errors && <p className={styles.error}>{errors}</p>}
{error !== "" && <p className={styles.error}>{error}</p>}
</form>
<p className={styles.login__link}>
Text
</p>
</div>
</article>
</section>
);
};
export default Register;
I tried several different approaches, however, none of them have worked for me so far. I ended up with something like that:
describe("Register /", () => {
it('register with proper credentials', () => {
const history = createMemoryHistory();
const component = mount(<Provider store={store}><Router history={history}><Register /></Router></Provider>);
component.find('input[name="firstname"]').simulate('change', {target: {name: 'firstname', value: 'Test'}});
component.find('input[name="lastname"]').simulate('change', {target: {name: 'lastname', value: 'Test'}});
component.find('input[name="password"]').simulate('change', {target: {name: 'password', value: 'TestSecret'}});
component.find('input[name="email"]').simulate('change', {target: {name: 'email', value: 'test#test.com'}});
})
After that I wanted to assert if the fields are filled in correctly and if the handleRegister is called but the fields are returning an empty string ("{}") and that's where I got stuck. What am I doing wrong?

value of the option selected not passing in react

Trying to get a response when clicking and store the selected option from the selected value genre, not sure how to add a select new state variable or a post method. When the option is clicked the click handler is not called with the selected options so that we can add it to the state. My genre is also seen as undefined when I don't click on the the text type.
Feedback.js
import React, { useState } from "react";
import axios from "axios";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.min.css";
import Layout from "./Layout";
const Feedback = () => {
const [values, setValues] = useState({
name: "",
artist: "",
songTitle: "",
email: "",
message: "",
phone: "",
genre: "",
uploadedFiles: [],
buttonText: "Submit",
uploadPhotosButtonText: "Upload files",
});
// destructure state variables
const {
name,
artist,
songTitle,
label,
email,
message,
phone,
genre,
uploadedFiles,
buttonText,
uploadPhotosButtonText,
} = values;
// destructure env variables
const {
REACT_APP_API,
REACT_APP_CLOUDINARY_CLOUD_NAME,
REACT_APP_CLOUDINARY_UPLOAD_SECRET,
} = process.env;
// event handler
const handleChange = (name) => (event) => {
setValues({ ...values, [name]: event.target.value });
};
const handleSubmit = (event) => {
event.preventDefault();
setValues({ ...values, buttonText: "...sending" });
// send to backend for email
console.table({ name, email, phone, message, uploadedFiles, genre });
axios({
method: "POST",
url: `${REACT_APP_API}/feedback`,
data: {
name,
artist,
songTitle,
label,
email,
genre,
phone,
message,
uploadedFiles,
},
})
.then((response) => {
// console.log('feedback submit response', response);
if (response.data.success) toast.success("Thanks for your feedback");
setValues({
...values,
name: "",
artist: "",
songTitle: "",
label: "",
phone: "",
email: "",
genre: "",
message: "",
uploadedFiles: [],
buttonText: "Submitted",
uploadPhotosButtonText: "Uploaded",
});
})
.catch((error) => {
// console.log('feedback submit error', error.response);
if (error.response.data.error) toast.error("Failed! Try again!");
});
};
function onChangeInput(value) {
console.log(value);
}
const uploadWidget = () => {
window.cloudinary.openUploadWidget(
{
cloud_name: REACT_APP_CLOUDINARY_CLOUD_NAME,
upload_preset: REACT_APP_CLOUDINARY_UPLOAD_SECRET,
tags: ["ebooks"],
},
function (error, result) {
// console.log(result);
setValues({
...values,
uploadedFiles: result,
uploadPhotosButtonText: `${
result ? result.length : 0
} Photos uploaded`,
});
}
);
};
const feedbackForm = () => (
<React.Fragment>
<div className="form-group">
<button
onClick={() => uploadWidget()}
className="btn btn-outline-secondary btn-block p-5"
>
{uploadPhotosButtonText}
</button>
</div>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label className="text-muted">Description</label>
<textarea
onChange={handleChange("message")}
type="text"
className="form-control"
value={message}
required
></textarea>
</div>
<div className="form-group">
<label className="text-muted">Your Name</label>
<input
className="form-control"
type="text"
onChange={handleChange("name")}
value={name}
required
/>
</div>
<div className="form-group">
<label className="text-muted">Your Email</label>
<input
className="form-control"
type="email"
onChange={handleChange("email")}
value={email}
required
/>
</div>
<div className="form-group">
<label className="text-muted">Your Phone</label>
<input
className="form-control"
type="number"
onChange={handleChange("phone")}
value={phone}
required
/>
</div>
<div className="form-group">
<label className="text-muted">Artist Name</label>
<input
className="form-control"
type="text"
onChange={handleChange("artist")}
value={artist}
required
/>
</div>
<div className="form-group">
<label className="text-muted">Song Title</label>
<input
className="form-control"
type="text"
onChange={handleChange("songTitle")}
value={songTitle}
required
/>
</div>
<div className="form-group">
<label className="text-muted">Record Label Name</label>
<input
className="form-control"
type="text"
onChange={handleChange("label")}
value={label}
/>
</div>
<div className="form-group">
<label className="text-muted">Music Genre:</label>
<select
className="form-control"
value={genre}
type="select"
onChange={handleChange("genre")}
>
<option value="steak">Steak</option>
<option value="sandwich">Sandwich</option>
<option value="dumpling">Dumpling</option>
</select>
</div>
<button className="btn btn-outline-primary btn-block">
{buttonText}
</button>
</form>
</React.Fragment>
);
return (
<Layout>
<ToastContainer />
<div className="container text-center">
<h1 className="p-5">Feedback Online</h1>
</div>
<div className="container col-md-8 offset-md-2">{feedbackForm()}</div>
<br />
<br />
<br />
</Layout>
);
};
export default Feedback;

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