Sending multipart Form in ReactJS vs Postman - javascript

I've created a registration api that works fine using postman, I added some values there plus image an everything stores correctly, data an image, Later I started my React form and only text works, image is not currently sent to the api I guess.
Doing console.log() api in Node:
console.log(req.files);
with React Form: []
with Postman: an array of object that perfectly works
req.files output using postman
{
fieldname: 'images',
originalname: 'Screen Shot 2021-02-22 at 17.18.41.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'uploads/',
filename: '091f77f82fb805b1ede9f23205cc578e',
path: 'uploads/091f77f82fb805b1ede9f23205cc578e',
size: 37052
}
Here's my react classes:
httpService.js
import axios from "axios";
import { toast } from "react-toastify";
axios.interceptors.response.use(null, (error) => {
const expectedError =
error.response &&
error.response.status >= 400 &&
error.response.status < 500;
if (!expectedError) {
console.log("Loggind the error: ", error);
toast("An unexpected error ocurred.");
}
return Promise.reject(error);
});
export default {
post: axios.post
};
itemService.js
export function saveItem(item) {
const headers = {
'Content-Type': 'multipart/form-data',
};
return http.post(MY_ENDPOINT, item, headers);
}
itemForm.js
import React from "react";
import Joi from "joi-browser";
import Form from "./common/form";
import { saveItem } from "../services/itemService";
class ItemForm extends Form {
state = {
data: { title: "", description: "", category: "", images: "" },
categories: [],
errors: {},
};
schema = {
_id: Joi.string(),
title: Joi.string().required().label("Title"),
description: Joi.string().required().label("Description"),
category: Joi.string().required().label("Category"),
images: Joi.required().label("Image"),
};
doSubmit = async () => {
console.log('form data> ', this.state.data); // This shows the correct object.
let formData = new FormData();
formData.append('title', this.state.data.title);
formData.append('description', this.state.data.description);
formData.append('category', this.state.data.category);
formData.append('images', this.state.data.images);
try {
await saveItem(formData);
} catch (ex) {
}
};
render() {
return (
<div>
<h1>New item</h1>
<form onSubmit={this.handleSubmit}>
{this.renderInput("title", "Title")}
{this.renderInput("description", "Description")}
{this.renderSelect(
"category",
"title",
"Category",
this.state.categories
)}
{this.renderInputFile("images", "images", "file", false)}
{this.renderButton("Register")}
</form>
</div>
);
}
}
export default ItemForm;
form.jsx (The extended class)
import React, { Component } from "react";
import Joi from "joi-browser";
import Input from "./input";
import Select from "./select";
class Form extends Component {
state = {
data: {},
errors: {},
};
validate = () => {
const options = { abortEarly: false };
const { error } = Joi.validate(this.state.data, this.schema, options);
if (!error) return null;
const errors = {};
for (let item of error.details) errors[item.path[0]] = item.message;
return errors;
};
validateProperty = ({ name, value }) => {
const obj = { [name]: value };
const schema = { [name]: this.schema[name] };
const { error } = Joi.validate(obj, schema);
return error ? error.details[0].message : null;
};
handleSubmit = (e) => {
e.preventDefault();
const errors = this.validate();
this.setState({ errors: errors || {} });
if (errors) return;
this.doSubmit();
};
handleChange = ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMessage = this.validateProperty(input);
if (errorMessage) errors[input.name] = errorMessage;
else delete errors[input.name];
const data = { ...this.state.data };
data[input.name] = input.value;
this.setState({ data, errors });
};
handleInputFileChange = ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMessage = this.validateProperty(input);
if (errorMessage) errors[input.name] = errorMessage;
else delete errors[input.name];
const data = { ...this.state.data };
data[input.name] = input.value;
this.setState({ data, errors });
};
renderButton(label) {
return (
<button className="btn btn-primary" disabled={this.validate()}>
{label}
</button>
);
}
renderInput(name, label, type = "text", multiple = false) {
const { data, errors } = this.state;
return (
<Input
type={type}
name={name}
value={data[name]}
label={label}
onChange={this.handleChange}
error={errors[name]}
multiple={multiple}
/>
);
}
renderSelect(name, contentField, label, options) {
const { data, errors } = this.state;
return (
<Select
name={name}
value={data[name]}
label={label}
contentField={contentField}
options={options}
onChange={this.handleChange}
error={errors[name]}
/>
);
}
renderInputFile(name, label, type = "text", multiple = false) {
const { data, errors } = this.state;
return (
<Input
type={type}
name={name}
value={data[name]}
label={label}
onChange={this.handleInputFileChange}
error={errors[name]}
multiple={multiple}
accept="image/*"
/>
);
}
}
export default Form;

Related

upload image and text with form

I'm learning React with Strapi.
I made a form to add data to the database. I manage to send my img in the upload api and the text data in the Animals api, however, I can't manage to link the two. Ie to make sure that the image also goes on the animal that I add.
Thank you all for your future help.
import React from "react";
import { useForm } from "react-hook-form";
import axios from "axios";
import React from "react";
import { useForm } from "react-hook-form";
import axios from "axios";
class AddAnimal extends React.Component {
constructor(props) {
super(props);
this.state = {
modifiedData: {
nom: '',
description: '',
image : '',
races: [],
images :[],
},
allRaces: [],
error: null,
};
}
onImageChange = event => {
console.log(event.target.files);
this.setState({
images: event.target.files,
});
};
componentDidMount = async () => {
try {
const response = await axios.get('http://localhost:1337/api/races');
this.setState({ allRaces: response.data.data });
} catch (error) {
this.setState({ error });
}
};
handleInputChange = ({ target: { name, value } }) => {
this.setState(prev => ({
...prev,
modifiedData: {
...prev.modifiedData,
[name]: value,
},
}));
};
handleSubmit = async e => {
e.preventDefault();
const formData = new FormData();
Array.from(this.state.images).forEach(image => {
formData.append('files', image);
});
try {
const response = await axios.post('http://localhost:1337/api/animaux?populate=*',
{
"data": {
nom: this.state.modifiedData.nom,
Description: this.state.modifiedData.description,
image : this.images,
races: this.state.modifiedData.races
}
})
axios
.post(`http://localhost:1337/api/upload`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
})
console.log(response);
} catch (error) {
this.setState({ error });
}
};
renderCheckbox = race => {
const {
modifiedData: { races },
} = this.state;
const isChecked = races.includes(race.id);
const handleChange = () => {
if (!races.includes(race.id)) {
this.handleInputChange({
target: { name: 'races', value: races.concat(race.id) },
});
} else {
this.handleInputChange({
target: {
name: 'races',
value: races.filter(v => v !== race.id),
},
});
}
};
return (
<div key={race.id}>
<label htmlFor={race.id}>{race.attributes.nom}</label>
<input
type="checkbox"
checked={isChecked}
onChange={handleChange}
name="races"
id={race.id}
/>
</div>
);
};
render() {
const { error, allRaces, modifiedData } = this.state;
if (error) {
return <div>An error occured: {error.message}</div>;
}
return (
<div className="App">
<form onSubmit={this.handleSubmit}>
<h3>Ajouter un animal</h3>
<br />
<label>
Name:
<input
type="text"
name="nom"
onChange={this.handleInputChange}
value={modifiedData.name}
/>
</label>
<label>
Description:
<input
type="text"
name="description"
onChange={this.handleInputChange}
value={modifiedData.description}
/>
</label>
<input type="file" name="images" value={this.images} onChange={this.onImageChange}/>
<div>
<br />
<b>Select races</b>
{allRaces.map(this.renderCheckbox)}
</div>
<br />
<button type="submit">Ajouter</button>
</form>
</div>
);
}
}
export default AddAnimal;

Getting value of SetState not updated in react

I am having a callback function which updates my array in the SetState and it works fine as I can see in the console log in the handleDaysValueChange all is good. But when I try to access it in another function i.e handleValuesChange the values are not up to date. I am missing a async or something.
import React from 'react';
import PropTypes from 'prop-types';
import AppService from 'services/app-service';
import Enum from 'enum';
import { ApiData } from 'data';
import { isEmpty, boolValue } from 'core/type-check';
import { notifySuccess } from 'core/errors';
import { withDrawerForm } from 'components/_hoc';
import { ExtendedAvatar } from 'components/extended-antd';
import { Tabs } from 'antd';
import { FormTemplateAudit } from '../templates';
import ShiftRosterFormDetails from './shiftroster-form-details';
import Item from 'antd/lib/list/Item';
const { TabPane } = Tabs;
class ShiftRosterForm extends React.Component {
constructor(props) {
super(props);
this.formInputRef = React.createRef();
this.state = {
daysValue:[]
};
this.handleDaysValueChange = this.handleDaysValueChange.bind(this);
}
handleDaysValueChange(daysValues) {
this.setState({ daysValue: daysValues }, () => {
console.log("Data" + this.props.customData);
console.log("Hello World " + JSON.stringify(this.state.daysValue));
});
}
componentDidMount() {
// Update the parent form state with a reference to the
// formInputRef so we can call this later for validation
// before saving the data to the server.
const { onFormStateChange } = this.props;
onFormStateChange({ formInputRef: this.formInputRef.current });
}
//Shift Roster Detail
handleValuesChange = (props, changedValues, allValues) => {
// Update the parent form state with the changed item
// details and mark the form as dirty
const { itemData, onFormStateChange } = this.props;
console.log("Hey" + this.state.daysValue);
onFormStateChange({
isFormDirty: true,
itemData: {
...itemData,
...changedValues,
...this.state.daysValue
}
});
}
render() {
const { itemData, customData } = this.props;
const isSR = (!isEmpty(itemData) && itemData.id > 0);
return (
<Tabs className="bhp-tabs" defaultActiveKey="1" animated={false}>
<TabPane key="1" tab="Details">
<ShiftRosterFormDetails
ref={this.formInputRef}
dataSource={itemData}
onValuesChange={this.handleValuesChange}
handleDaysValueChange={this.handleDaysValueChange}
/>
</TabPane>
<TabPane key="2" tab="Audit" disabled={!isSR}>
<FormTemplateAudit
itemData={itemData}
/>
</TabPane>
</Tabs>
);
}
}
ShiftRosterForm.propTypes = {
itemId: PropTypes.number, // Passed in by the HOC. The loaded Shift Roster id
itemData: PropTypes.object, // Passed in by the HOC. The loaded Shift Roster data
customData: PropTypes.object, // Temporary store to hold the changed Shift Roster
isFormDirty: PropTypes.bool, // Passed in by the HOC. Flags if the parent form is dirty
isLoading: PropTypes.bool, // Passed in by the HOC. Flags if the parent form is loading
daysValue: PropTypes.object,
onFormStateChange: PropTypes.func // Passed in by the HOC. Callback to update the parent form state.
};
ShiftRosterForm.defaultProps = {
itemId: -1,
itemData: {},
customData: {},
isFormDirty: false,
isLoading: false,
daysValue: {},
onFormStateChange() { }
};
const ShiftRosterFormTitle = ({ data }) => {
const name = (!isEmpty(data) && data.id > 0) ? `${data.name}` : 'New Shift Roster';//`${data.name}`
return isEmpty(data)
? <ExtendedAvatar type="icon" size="large" />
: <span><ExtendedAvatar name={name} type="letter" size="large" />{name}</span>
}
const saveShiftRoster = (shiftrosterId, shiftroster, rosterdays) => {
return ApiData.saveShiftRoster(shiftrosterId, shiftroster, rosterdays)
.then(response => {
notifySuccess('Save Successful', 'Site Work Package saved successfully');
return response;
})
.catch(error => {
throw error;
});
}
const saveForm = (formState, setFormState) => {
const { isFormDirty, itemData, customData, formInputRef } = formState;
const typeName = "Dynamic";
const actualType = itemData.type;
let rosterdays = [];
if (actualType !== typeName) {
rosterdays = GetDaysForRoster(itemData);
console.log("My Values" + JSON.stringify(rosterdays));
}
const shiftRosterId = itemData.id;
const isExistingShiftRoster = shiftRosterId > 0;
return new Promise((resolve, reject) => {
if (isExistingShiftRoster && !isFormDirty) {
// No Changes
notifySuccess('Save Successful', 'Site Work Package saved successfully');
resolve(itemData);
}
else {
// Validate and Save
formInputRef.validateFields((error, values) => {
if (!error) {
// Form validated successfully.
// Save form changes
const shiftrosterRecord = saveShiftRoster(shiftRosterId, values, rosterdays);
resolve(shiftrosterRecord);
}
else {
// Form validation error.
// Return data as is.
resolve(itemData);
}
});
}
});
}
const GetDaysForRoster = (itemsData) => {
const result = [];
const keys = Object.keys(itemsData);
for (const k in keys) {
if (Number(k) == k) {
result[k] = itemsData[k]
}
}
return result.filter(function (el) { return el != null });
}
const WrappedShiftRosterForm = withDrawerForm({
containerClassName: 'bhp-equipment-type-form',
title: (record) => <ShiftRosterFormTitle data={record} />,
onLoad: (itemId, setFormState) => ApiData.getShiftRoster(itemId),
onSave: (formState, setFormState) => { return saveForm(formState, setFormState); },
canView: () => AppService.hasAccess({ [Enum.SecurityModule.EquipmentTypeDetails]: [Enum.SecurityPermission.Read] }),
canCreate: () => AppService.hasAccess({ [Enum.SecurityModule.EquipmentTypeDetails]: [Enum.SecurityPermission.Create] }),
canUpdate: () => AppService.hasAccess({ [Enum.SecurityModule.EquipmentTypeDetails]: [Enum.SecurityPermission.Update] })
})(ShiftRosterForm);
WrappedShiftRosterForm.propTypes = {
containerClassName: PropTypes.string,
itemId: PropTypes.number,
visible: PropTypes.bool,
onSave: PropTypes.func,
onClose: PropTypes.func
};
WrappedShiftRosterForm.defaultProps = {
containerClassName: null,
itemId: -1,
visible: false,
onSave() { },
onClose() { }
};
export default WrappedShiftRosterForm;
//ShiftRosterFormDetails
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { ApiData } from 'data';
import { Form, Input, Select, Button, space, InputNumber } from 'antd';
import ShiftDays from './shiftdays'
const ShiftRosterFormDetails = ({ form, dataSource, onValueChange, handleDaysValueChange }) => {
const { getFieldDecorator } = form;
const formLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
},
};
console.log("My datasource" + dataSource.shiftRosterDays);
//const daysRoster = dataSource.shiftRosterDays || [{ daysIn: 1, daysOut: 1, category: "Day Shift" }];
const [inputList, setInputList] = useState([{ daysIn: 1, daysOut: 1, category: "Day Shift" }]);
const [selectedType, setSelectedType] = useState(dataSource.type || 'Fixed Single');
const [isTotalDaysRequired, SetTotalDaysRequired] = useState(dataSource.type === 'Dynamic' ? true : false);
const [isTotalDaysRequiredMessage, setIsTotalDaysRequiredMessage] = useState(dataSource.type === 'Dynamic' ? 'Please enter the Total Days' : '');
const handleTypeChanged = (value, e) => {
setSelectedType(value);
if (value === "Dynamic") {
SetTotalDaysRequired(true);
setIsTotalDaysRequiredMessage('Please enter the Total Days');
}
if (value === "Fixed Single") {
if (inputList.length > 1) {
const list = [...inputList];
console.log("Total" + inputList.length);
list.splice(1, inputList.length);
setInputList(list);
console.log("My List" + JSON.stringify(list));
console.log("Input List" + JSON.stringify(inputList));
handleDaysValueChange(list);
}
}
else {
SetTotalDaysRequired(false);
setIsTotalDaysRequiredMessage('');
}
};
return (
<div className='bhp-equipment-type-form-details bhp-content-box-shadow'>
<Form {...formLayout}>
<Form.Item label='Name' hasFeedback>
{getFieldDecorator('name', {
initialValue: dataSource.name,
rules: [{
required: true,
message: 'Please enter the Name'
}],
})(
//disabled={dataSource.id > 0}
<Input placeholder='Name' />
)}
</Form.Item>
<Form.Item label='Status' hasFeedback>
{getFieldDecorator('status', {
initialValue: dataSource.status,
rules: [{
required: true,
message: 'Please enter the Status'
}],
})(
<Select>
<Select.Option value="Active">Active</Select.Option>
<Select.Option value="InActive">InActive</Select.Option>
</Select>
)}
</Form.Item>
<Form.Item label='Type' hasFeedback>
{getFieldDecorator('type', {
initialValue: dataSource.type || 'Fixed Single',
rules: [{
required: true,
message: 'Please select the Type'
}],
})(
<Select onChange={handleTypeChanged}>
<Select.Option value="Fixed Single">Fixed Single</Select.Option>
<Select.Option value="Fixed Multiple">Fixed Multiple</Select.Option>
<Select.Option value="Dynamic">Dynamic</Select.Option>
</Select>
)}
</Form.Item>
<Form.Item label='Total Days' hasFeedback style={selectedType === 'Dynamic' ? { display: '' } : { display: 'none' }}>
{getFieldDecorator('totaldays', {
initialValue: dataSource.totalDays,
rules: [{
required: isTotalDaysRequired,
message: isTotalDaysRequiredMessage
}],
})(
<InputNumber min={1} max={365} />
)}
</Form.Item>
<ShiftDays inputList={inputList} setInputList={setInputList} selectedType={selectedType} handleDaysValueChange={handleDaysValueChange} getFieldDecorator={getFieldDecorator} />
</Form>
</div>
)};
const onFieldsChange = (props, changedFields, allFields) => {
if (props.onFieldsChange) {
props.onFieldsChange(props, changedFields, allFields);
}
};
const onValuesChange = (props, changedValues, allValues) => {
if (props.onValuesChange) {
props.onValuesChange(props, changedValues, allValues);
}
};
ShiftRosterFormDetails.propTypes = {
form: PropTypes.object,
dataSource: PropTypes.object,
onFieldsChange: PropTypes.func,
onValuesChange: PropTypes.func
};
ShiftRosterFormDetails.defaultProps = {
form: {},
dataSource: {},
onFieldsChange() { },
onValuesChange() { }
};
export default Form.create({
onValuesChange,
onFieldsChange
})(ShiftRosterFormDetails);

In React, can a Parent class access fields from a Child class?

I am learning React. In a tutorial I saw online regarding handling Form submissions, there is a component Form which is being extended by another component LoginForm.
Form.jsx:
import { React, Component } from "react";
import Joi from "joi-browser";
class Form extends Component {
state = {
data: {},
errors: {},
};
validate = () => {
const options = { abortEarly: false };
const { error } = Joi.validate(this.state.data, this.schema, options);
if (!error) return null;
const errors = {};
for (let item of error.details) errors[item.path[0]] = item.message;
return errors;
};
validateProperty = ({ name, value }) => {
const obj = { [name]: value };
const schema = { [name]: this.schema[name] };
const { error } = Joi.validate(obj, schema);
return error ? error.details[0].message : null;
};
handleSubmit = (e) => {
e.preventDefault();
const errors = this.validate();
this.setState({ errors: errors || {} });
if (errors) return;
this.doSubmit();
};
handleChange = ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMessage = this.validateProperty(input);
if (errorMessage) errors[input.name] = errorMessage;
else delete errors[input.name];
const data = { ...this.state.data };
data[input.name] = input.value;
this.setState({ data, errors });
};
}
export default Form;
LoginForm.jsx:
import React, { Component } from "react";
import Form from "./common/form";
import Joi from "joi-browser";
import Input from "./common/input";
class LoginForm extends Form {
state = {
data: { username: "", password: "" },
errors: {},
};
schema = {
username: Joi.string().required().label("Username"),
password: Joi.string().required().label("Password"),
};
doSubmit = () => {
// call the server
console.log("Submitted");
};
render() {
const { data, errors } = this.state;
return (
<div>
<h1>Login</h1>
<form onSubmit={this.handleSubmit}>
<Input
name="username"
value={data.username}
label="Username"
onChange={this.handleChange}
error={errors.username}
/>
<Input
name="password"
value={data.password}
label="Password"
onChange={this.handleChange}
error={errors.password}
/>
<button disabled={this.validate()} className="btn btn-primary">
Login
</button>
</form>
</div>
);
}
}
export default LoginForm;
Since LoginForm is extending Form, in the validate() function, how can Form be using properties like this.schema, if schema is defined in LoginForm.jsx?

'http://localhost:8000' has been blocked by CORS policy

I'm working on the Pro MERN Stack: Full Stack Web App Development with Mongo, Express, React, and Node book, and I ran into CORS policy errors whenever I tried to go back to my homepage. It looks like the error is coming from my graphqlFetch file but I'm not so sure what the issue is. Any insight would be extremely helpful!!
CORS policy error message
GraphQLFetch error
1:
My GraphQLFetch code:
import fetch from 'isomorphic-fetch';
const dateRegex = new RegExp('^\\d\\d\\d\\d-\\d\\d-\\d\\d');
function jsonDateReviver(key, value) {
if (dateRegex.test(value)) return new Date(value);
return value;
}
export default async function
graphQLFetch(query, variables = {}, showError = null, cookie = null) {
const apiEndpoint = (__isBrowser__) // eslint-disable-line no-undef
? window.ENV.UI_API_ENDPOINT
: process.env.UI_SERVER_API_ENDPOINT;
try {
const headers = { 'Content-Type': 'application/json' };
if (cookie) headers.Cookie = cookie;
const response = await fetch(apiEndpoint, {
method: 'POST',
credentials: 'include',
headers,
body: JSON.stringify({ query, variables }),
});
const body = await response.text();
const result = JSON.parse(body, jsonDateReviver);
if (result.errors) {
const error = result.errors[0];
if (error.extensions.code === 'BAD_USER_INPUT') {
const details = error.extensions.exception.errors.join('\n ');
if (showError) showError(`${error.message}:\n ${details}`);
} else if (showError) {
showError(`${error.extensions.code}: ${error.message}`);
}
}
return result.data;
} catch (e) {
if (showError) showError(`Error in sending data to server: ${e.message}`);
return null;
}
}
My IssueEdit file
import React from 'react';
import { Link } from 'react-router-dom';
import { LinkContainer } from 'react-router-bootstrap';
import {
Col, Panel, Form, FormGroup, FormControl, ControlLabel,
ButtonToolbar, Button, Alert,
} from 'react-bootstrap';
import graphQLFetch from './graphQLFetch.js';
import NumInput from './NumInput.jsx';
import DateInput from './DateInput.jsx';
import TextInput from './TextInput.jsx';
import Toast from './Toast.jsx';
import store from './store.js';
export default class IssueEdit extends React.Component {
static async fetchData(match, search, showError) {
const query = `query issue($id: Int!) {
issue(id: $id) {
id title status owner
effort created due description
}
}`;
const { params: { id } } = match;
const result = await graphQLFetch(query, { id }, showError);
return result;
}
constructor() {
super();
const issue = store.initialData ? store.initialData.issue : null;
delete store.initialData;
this.state = {
issue,
invalidFields: {},
showingValidation: false,
toastVisible: false,
toastMessage: '',
toastType: 'success',
};
this.onChange = this.onChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.onValidityChange = this.onValidityChange.bind(this);
this.dismissValidation = this.dismissValidation.bind(this);
this.showValidation = this.showValidation.bind(this);
this.showSuccess = this.showSuccess.bind(this);
this.showError = this.showError.bind(this);
this.dismissToast = this.dismissToast.bind(this);
}
componentDidMount() {
const { issue } = this.state;
if (issue == null) this.loadData();
}
componentDidUpdate(prevProps) {
const { match: { params: { id: prevId } } } = prevProps;
const { match: { params: { id } } } = this.props;
if (id !== prevId) {
this.loadData();
}
}
onChange(event, naturalValue) {
const { name, value: textValue } = event.target;
const value = naturalValue === undefined ? textValue : naturalValue;
this.setState(prevState => ({
issue: { ...prevState.issue, [name]: value },
}));
}
onValidityChange(event, valid) {
const { name } = event.target;
this.setState((prevState) => {
const invalidFields = { ...prevState.invalidFields, [name]: !valid };
if (valid) delete invalidFields[name];
return { invalidFields };
});
}
async handleSubmit(e) {
e.preventDefault();
this.showValidation();
const { issue, invalidFields } = this.state;
if (Object.keys(invalidFields).length !== 0) return;
const query = `mutation issueUpdate(
$id: Int!
$changes: IssueUpdateInputs!
) {
issueUpdate(
id: $id
changes: $changes
) {
id title status owner
effort created due description
}
}`;
const { id, created, ...changes } = issue;
const data = await graphQLFetch(query, { changes, id }, this.showError);
if (data) {
this.setState({ issue: data.issueUpdate });
this.showSuccess('Updated issue successfully');
}
}
async loadData() {
const { match } = this.props;
const data = await IssueEdit.fetchData(match, null, this.showError);
this.setState({ issue: data ? data.issue : {}, invalidFields: {} });
}
showValidation() {
this.setState({ showingValidation: true });
}
dismissValidation() {
this.setState({ showingValidation: false });
}
showSuccess(message) {
this.setState({
toastVisible: true, toastMessage: message, toastType: 'success',
});
}
showError(message) {
this.setState({
toastVisible: true, toastMessage: message, toastType: 'danger',
});
}
dismissToast() {
this.setState({ toastVisible: false });
}
render() {
const { issue } = this.state;
if (issue == null) return null;
const { issue: { id } } = this.state;
const { match: { params: { id: propsId } } } = this.props;
if (id == null) {
if (propsId != null) {
return <h3>{`Issue with ID ${propsId} not found.`}</h3>;
}
return null;
}
const { invalidFields, showingValidation } = this.state;
let validationMessage;
if (Object.keys(invalidFields).length !== 0 && showingValidation) {
validationMessage = (
<Alert bsStyle="danger" onDismiss={this.dismissValidation}>
Please correct invalid fields before submitting.
</Alert>
);
}
const { issue: { title, status } } = this.state;
const { issue: { owner, effort, description } } = this.state;
const { issue: { created, due } } = this.state;
const { toastVisible, toastMessage, toastType } = this.state;
return (
<Panel>
<Panel.Heading>
<Panel.Title>{`Editing issue: ${id}`}</Panel.Title>
</Panel.Heading>
<Panel.Body>
<Form horizontal onSubmit={this.handleSubmit}>
<FormGroup>
<Col componentClass={ControlLabel} sm={3}>Created</Col>
<Col sm={9}>
<FormControl.Static>
{created.toDateString()}
</FormControl.Static>
</Col>
</FormGroup>
<FormGroup>
<Col componentClass={ControlLabel} sm={3}>Status</Col>
<Col sm={9}>
<FormControl
componentClass="select"
name="status"
value={status}
onChange={this.onChange}
>
<option value="New">New</option>
<option value="Assigned">Assigned</option>
<option value="Fixed">Fixed</option>
<option value="Closed">Closed</option>
</FormControl>
</Col>
</FormGroup>
<FormGroup>
<Col componentClass={ControlLabel} sm={3}>Owner</Col>
<Col sm={9}>
<FormControl
componentClass={TextInput}
name="owner"
value={owner}
onChange={this.onChange}
key={id}
/>
</Col>
</FormGroup>
<FormGroup>
<Col componentClass={ControlLabel} sm={3}>Effort</Col>
<Col sm={9}>
<FormControl
componentClass={NumInput}
name="effort"
value={effort}
onChange={this.onChange}
key={id}
/>
</Col>
</FormGroup>
<FormGroup validationState={
invalidFields.due ? 'error' : null
}
>
<Col componentClass={ControlLabel} sm={3}>Due</Col>
<Col sm={9}>
<FormControl
componentClass={DateInput}
onValidityChange={this.onValidityChange}
name="due"
value={due}
onChange={this.onChange}
key={id}
/>
<FormControl.Feedback />
</Col>
</FormGroup>
<FormGroup>
<Col componentClass={ControlLabel} sm={3}>Title</Col>
<Col sm={9}>
<FormControl
componentClass={TextInput}
size={50}
name="title"
value={title}
onChange={this.onChange}
key={id}
/>
</Col>
</FormGroup>
<FormGroup>
<Col componentClass={ControlLabel} sm={3}>Description</Col>
<Col sm={9}>
<FormControl
componentClass={TextInput}
tag="textarea"
rows={4}
cols={50}
name="description"
value={description}
onChange={this.onChange}
key={id}
/>
</Col>
</FormGroup>
<FormGroup>
<Col smOffset={3} sm={6}>
<ButtonToolbar>
<Button bsStyle="primary" type="submit">Submit</Button>
<LinkContainer to="/issues">
<Button bsStyle="link">Back</Button>
</LinkContainer>
</ButtonToolbar>
</Col>
</FormGroup>
<FormGroup>
<Col smOffset={3} sm={9}>{validationMessage}</Col>
</FormGroup>
</Form>
</Panel.Body>
<Panel.Footer>
<Link to={`/edit/${id - 1}`}>Prev</Link>
{' | '}
<Link to={`/edit/${id + 1}`}>Next</Link>
</Panel.Footer>
<Toast
showing={toastVisible}
onDismiss={this.dismissToast}
bsStyle={toastType}
>
{toastMessage}
</Toast>
</Panel>
);
}
}
My IssueList file:
import React from 'react';
import URLSearchParams from 'url-search-params';
import { Panel } from 'react-bootstrap';
import IssueFilter from './IssueFilter.jsx';
import IssueTable from './IssueTable.jsx';
import IssueDetail from './IssueDetail.jsx';
import graphQLFetch from './graphQLFetch.js';
import withToast from './withToast.jsx';
import store from './store.js';
class IssueList extends React.Component {
static async fetchData(match, search, showError) {
const params = new URLSearchParams(search);
const vars = { hasSelection: false, selectedId: 0 };
if (params.get('status')) vars.status = params.get('status');
const effortMin = parseInt(params.get('effortMin'), 10);
if (!Number.isNaN(effortMin)) vars.effortMin = effortMin;
const effortMax = parseInt(params.get('effortMax'), 10);
if (!Number.isNaN(effortMax)) vars.effortMax = effortMax;
const { params: { id } } = match;
const idInt = parseInt(id, 10);
if (!Number.isNaN(idInt)) {
vars.hasSelection = true;
vars.selectedId = idInt;
}
const query = `query issueList(
$status: StatusType
$effortMin: Int
$effortMax: Int
$hasSelection: Boolean!
$selectedId: Int!
) {
issueList(
status: $status
effortMin: $effortMin
effortMax: $effortMax
) {
id title status owner
created effort due
}
issue(id: $selectedId) #include (if : $hasSelection) {
id description
}
}`;
const data = await graphQLFetch(query, vars, showError);
return data;
}
constructor() {
super();
const issues = store.initialData ? store.initialData.issueList : null;
const selectedIssue = store.initialData
? store.initialData.issue
: null;
delete store.initialData;
this.state = {
issues,
selectedIssue,
};
this.closeIssue = this.closeIssue.bind(this);
this.deleteIssue = this.deleteIssue.bind(this);
}
componentDidMount() {
const { issues } = this.state;
if (issues == null) this.loadData();
}
componentDidUpdate(prevProps) {
const {
location: { search: prevSearch },
match: { params: { id: prevId } },
} = prevProps;
const { location: { search }, match: { params: { id } } } = this.props;
if (prevSearch !== search || prevId !== id) {
this.loadData();
}
}
async loadData() {
const { location: { search }, match, showError } = this.props;
const data = await IssueList.fetchData(match, search, showError);
if (data) {
this.setState({ issues: data.issueList, selectedIssue: data.issue });
}
}
async closeIssue(index) {
const query = `mutation issueClose($id: Int!) {
issueUpdate(id: $id, changes: { status: Closed }) {
id title status owner
effort created due description
}
}`;
const { issues } = this.state;
const { showError } = this.props;
const data = await graphQLFetch(query, { id: issues[index].id },
showError);
if (data) {
this.setState((prevState) => {
const newList = [...prevState.issues];
newList[index] = data.issueUpdate;
return { issues: newList };
});
} else {
this.loadData();
}
}
async deleteIssue(index) {
const query = `mutation issueDelete($id: Int!) {
issueDelete(id: $id)
}`;
const { issues } = this.state;
const { location: { pathname, search }, history } = this.props;
const { id } = issues[index];
const { showSuccess, showError } = this.props;
const data = await graphQLFetch(query, { id }, showError);
if (data && data.issueDelete) {
this.setState((prevState) => {
const newList = [...prevState.issues];
if (pathname === `/issues/${id}`) {
history.push({ pathname: '/issues', search });
}
newList.splice(index, 1);
return { issues: newList };
});
showSuccess(`Deleted issue ${id} successfully.`);
} else {
this.loadData();
}
}
render() {
const { issues } = this.state;
if (issues == null) return null;
const { selectedIssue } = this.state;
return (
<React.Fragment>
<Panel>
<Panel.Heading>
<Panel.Title toggle>Filter</Panel.Title>
</Panel.Heading>
<Panel.Body collapsible>
<IssueFilter />
</Panel.Body>
</Panel>
<IssueTable
issues={issues}
closeIssue={this.closeIssue}
deleteIssue={this.deleteIssue}
/>
<IssueDetail issue={selectedIssue} />
</React.Fragment>
);
}
}
const IssueListWithToast = withToast(IssueList);
IssueListWithToast.fetchData = IssueList.fetchData;
export default IssueListWithToast;
You have to allow cross origin on your graphql server. For security reasons browsers didn't allow ajax request on cross origin. So lets say you're on localhost:3000 and your server is running on localhost:8000 so these two are on different origins.
So use cors module on Express server and allow Access-Control-Allow-Origin to "*"

Can't update the state in my react class component

I have 2 components:
Form
EmailForm (which extends Form)
I don't understand why my state "errors" is not updated whenI submit the form button?
When I check in the component react tool in chrome dev tools nothing happens.
Do you know what I didn't do correctly in the handleSubmit and validate function?
import React from 'react';
import Form from './form';
class EmailForm extends Form {
state = {
data: { email: '' },
errors: {}
};
render() {
return (
<div>
<p>
<i>Renseignez votre email pour être averti dès que l'application sera disponible :</i>
</p>
<form onSubmit={this.handleSubmit}>
{this.renderInput('email', 'Email')}
{this.renderButton('Envoyer')}
</form>
</div>
);
}
}
export default EmailForm;
import React, { Component } from 'react';
import { ButtonPrimary } from './buttonPrimary';
import { ButtonTransparent } from './buttonTransparent';
import Input from './input2';
interface IFormState {
data: any;
errors: any;
}
class Form extends React.PureComponent<{}, IFormState> {
state = {
data: {},
errors: {}
};
validate = (): any => {
return { email: 'Email is required' };
};
validateProperty = (input: any) => {
console.log('validated Property');
};
handleSubmit = (e: any) => {
e.preventDefault();
const errors: any = this.validate();
this.setState({ errors });
if (errors) return;
this.doSubmit();
};
doSubmit = () => {
console.log('Submitted');
};
handleChange = ({ currentTarget: input }: any) => {
const errors: any = { ...this.state.errors };
const errorMessage: any = this.validateProperty(input);
if (errorMessage) errors[input.name] = errorMessage;
else delete errors[input.name];
const data: any = { ...this.state.data };
data[input.name] = input.value;
this.setState({ data, errors });
};
renderButton(label: string) {
return <ButtonPrimary disabled={!this.validate()}>{label}</ButtonPrimary>;
}
renderInput(name: string, label: string, type = 'email') {
const { data, errors }: any = this.state;
return (
<Input
autoFocus
type={type}
name={name}
value={data[name]}
label={label}
onChange={this.handleChange}
error={errors[name]}
/>
);
}
}
export default Form;

Categories

Resources