Why am I getting an error when creating a new news entry page? - javascript

I am working with GraphQL and React and I have next post form code:
const PostForm = props => {
// set the default state of the form
const [values, setValues] = useState();
// update the state when a user types in the form
const onChange = event => {
setValues({
...values,
[event.target.name]: event.target.value
});
};
return (
<Wrapper>
<Form
onSubmit={event => {
event.preventDefault();
props.action({
variables: {
...values
}
});
}}
>
<label htmlFor="title">Title Post:</label>
<input
required
type="text"
id="title"
name="title"
placeholder="title"
onChange={onChange}
/>
<label htmlFor="category">Category Post:</label>
<input
required
type="text"
id="category"
name="category"
placeholder="category"
onChange={onChange}
/>
<TextArea
required
type="text"
name="body"
placeholder="Post content"
onChange={onChange}
/>
<Button type="submit">Save</Button>
</Form>
</Wrapper>
);
};
This code I have in the new post page:
const NEW_POST = gql`
mutation createPost($title: String, $category: String, $body: String) {
createPost(title: $title, category: $category, body: $body) {
_id
title
createdAt
updatedAt
body
author {
name
}
comments{
text
}
}
}`;
const NewPost = props => {
useEffect(() => {
document.title = 'NewPost - Notedly';
});
const [ data, { loading, error } ] = useMutation(NEW_POST, {
onCompleted: data => {
props.history.push(`posts/${data.createPost._id}`);
}
});
return (
<React.Fragment>
{loading && <p> loading...</p>}
{error && <p>Error saving the note</p>}
{console.log(data)}
<PostForm action={data} />
</React.Fragment>
);
};
I have the following mutation code, for example:
mutation{
createPost(title: "my jobs", category: "6251ef28413373118838bbdd", body: "smdbsdfsjns"){
_id
title
category
{catname}
body
}
}
I don't understand why I am getting this error:
"Uncaught (in promise) Error: Network error: Response not successful: Received status code 400"

Related

Validating form inputs with React

I am trying to check that the required fields are not empty and making sure that the input type is correct.
const CreateSensor = () => {
const [deveui, setDeveui] = useState('');
const [location, setLocation] = useState('');
const [levelid, setLevel] = useState('');
const submitValue = () => {
let data = {deveui,location,levelid};
//POST method
fetch("api")
ClearFields();
}
function ClearFields(){
document.getElementById("dev").value = "";
document.getElementById("location").value = "";
document.getElementById("level").value = "";
}
return(
<>
<hr/>
<input type="text" id="dev" placeholder="deveui" onChange={e => setDeveui(e.target.value)} />
<input type="text" id="location"placeholder="Location" onChange={e => setLocation(e.target.value)} />
<input type="text" id="level" placeholder="Levelid" onChange={e => setLevel(e.target.value)} />
<button onClick={submitValue}>Submit</button>
</>
)
}
the submit button will check whether deveui is not empty and the levelid is set to an integer.
I have tried changing the input type for levelid to numbers but there is arrows on it which I feel is unnecessary.
I strongly recommend using a React form library. Here's an example with react-hook-form
import { useForm } from "react-hook-form";
const CreateSensor = () => {
const {
register,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm({ defaultValues: { deveui: "", location: "", levelid: "" } });
const submitValue = ({deveui, location, levelid}) => {
// exclude 'deveui' from fetch payload
const payload = { location, levelid }
// POST data to api, for example
fetch("https://myapi.com", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
// reset form state
}).then((response) => reset());
};
return (
<>
<hr />
<form onSubmit={handleSubmit(submitValue)}>
<input
{...register("deveui", { required: true })}
type="text"
id="dev"
placeholder="deveui"
/>
<input
{...register("location", { required: true })}
type="text"
id="location"
placeholder="Location"
/>
<input
{...register("levelid", { required: true })}
type="text"
id="level"
placeholder="Levelid"
/>
<button type="submit">Submit</button>
</form>
</>
);
}

Cannot read property 'map' of undefined Error when using axios.get inside useEffect hook

This is my edit-exercises component given below. So in my exercises component when I am clicking
edit to update my exercises with respect to its id, it has to render to edit-exercise component
but on rendering it gives above mentioned error. This is my component for the reference. In this, in
useEffect I am fetching my exercise with given id from the URL.
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import axios from "axios";
const EditExercise = (props) => {
const [userDetails, setUserDetails] = useState({
username: "",
description: "",
duration: 0,
date: new Date(),
users: [],
});
useEffect(() => { //This is the getting from backend
axios
.get("http://localhost:5000/exercises/"+props.match.params.id)
.then(res => {
setUserDetails({
username: res.data.username,
description: res.data.description,
duration: res.data.duration,
date: new Date(res.data.date),
})
})
.catch((err) => {
console.log(err);
});
axios
.get("http://localhost:5000/users/")
.then((res) => {
if (res.data.length > 0) {
setUserDetails({
users: res.data.map((user) => user.username),
});
}
})
.catch((err) => {
console.log(err);
});
},[props.match.params.id]);
const changeUsernameHandler = (e) => {
setUserDetails((prevState) => {
return {
...prevState,
username: e.target.value,
};
});
};
const changeDescriptionHandler = (e) => {
setUserDetails((prevState) => {
return {
...prevState,
description: e.target.value,
};
});
};
const changeDurationHandler = (e) => {
setUserDetails((prevState) => {
return {
...prevState,
duration: e.target.value,
};
});
};
const changeDateHandler = (date) => {
setUserDetails((prevState) => {
return {
...prevState,
date: date,
};
});
};
const onSubmitHandler = (e) => { //On submit this code will run
e.preventDefault();
const exercise = {
username: userDetails.username,
description: userDetails.description,
duration: userDetails.duration,
date: userDetails.date,
};
console.log(exercise);
axios
.post("http://localhost:5000/exercises/update/"+props.match.params.id, exercise)
.then((res) => console.log(res.data));
window.location = "/";
};
return (
<div>
<h3>Edit Exercise log</h3>
<form onSubmit={onSubmitHandler}>
<div className="form-group">
<label>Username: </label>
<select
required
className="form-control"
onChange={changeUsernameHandler}
value={userDetails.username}
>
{userDetails.users.map((user) => {
return (
<option key={user} value={user}>
{user}
</option>
);
})}
</select>
</div>
<div className="form-group">
<label>Description: </label>
<input
type="text"
required
className="form-control"
onChange={changeDescriptionHandler}
value={userDetails.description}
/>
</div>
<div className="form-group">
<label>Duration (in minutes): </label>
<input
type="number"
className="form-control"
onChange={changeDurationHandler}
value={userDetails.duration}
/>
</div>
<div className="form-group">
<label>Date: </label>
<div>
<DatePicker
onChange={changeDateHandler}
selected={userDetails.date}
/>
</div>
</div>
<div>
<input
type="submit"
value="Edit Exercise Log"
className="btn btn-primary"
/>
</div>
</form>
</div>
);
};
export default EditExercise;```
>Please suggest what can be done to render the edit-exercise component
Cannot read property 'map' of undefined
This error is thrown because the array you are trying to map doesn't exist. Please check if the array exists before you map the array.
users: res.data ? res.data.map((user) => user.username) : [],
And
{userDetails.users && userDetails.users.map((user) => {
return (
<option key={user} value={user}>
{user}
</option>
);
})}
Since axios.get returns a promise, and setUserDetails is set after the promise is returned, you need to be careful on when useEffect is triggered. Currently useEffect is triggered when props.match.params.id is changed.
There are 2 possible solutions for it:
Either, you can remove props.match.params.id from the useEffect second parameter.
Or you can this section outside the useEffect hook:
axios
.get("http://localhost:5000/users/")
.then((res) => {
if (res.data.length > 0) {
setUserDetails({
users: res.data.map((user) => user.username),
});
}
})
.catch((err) => {
console.log(err);
});

React Component not getting the updated state from the reducer

I am new to react and trying the redux hooks for the first time and got stuck in this issue for a day. Finally thought of putting in the forum. I am attaching the screenshots of the code here.
When I submit the form, the success scenario is working pretty fine. It is sending the request to the backend where the user is getting saved correctly to DB! The issue is with the error case, where the user submits a form with already taken email. In this case, the if block of (auth.error) is not invoking as the state is not getting an error object from the reducer.
const Signup = () => {
debug('In the Render shit!');
const auth = useSelector((state) => state, shallowEqual);
const dispatch = useDispatch();
const [values, setValues] = useState({
name: 'Rahul',
lastname: 'Kodu',
email: 'rahul.kodu#entr.co',
password: '12345',
success: false,
});
const { name, email, password, success, lastname } = values;
const handleChange = (event) => {
setValues({
...values,
[event.target.name]: event.target.value,
});
};
const handleSubmit = (e) => {
e.preventDefault();
//make thunk call here
debug('Submit Button Clicked');
const user = { name, email, password, lastname };
dispatch(signUpUser(user))
.then(() => {
debug('In then of thunk!');
//Finale
debug(auth);
if (auth.error) {
setValues({ ...values, success: false });
toast.error('Email Already Taken');
} else {
toast.info('🏃 Signed Up! Now Sign in!');
setValues({
email: '',
password: '',
name: '',
lastname: '',
success: true,
});
}
//finale
})
.catch((err) => {
debug('In err of thunk' + err);
});
};
const successMessage = () => {
if (success) {
return <Redirect to="/signin"></Redirect>;
}
};
const signUpForm = () => {
return (
<div className="row">
<div className="col-md-6 offset-sm-3 text-left">
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="name-field">Name</label>
<input
className="form-control"
type="text"
id="name-field"
name="name"
value={name}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="name-field">Last name</label>
<input
className="form-control"
type="text"
id="lastname-field"
name="lastname"
value={lastname}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="email-field">Email</label>
<input
className="form-control"
type="email"
id="email-field"
name="email"
value={email}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="password-field">Password</label>
<input
className="form-control"
type="password"
id="password-field"
name="password"
value={password}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<button
type="submit"
className="btn btn-sm btn-custom btn-block mt-5"
>
Sign-up!
</button>
</div>
</form>
</div>
</div>
);
};
return (
<Base title="Signup Page" description="Exisitng user? Goto Signin">
{JSON.stringify(auth)}
{signUpForm()}
{successMessage()}
</Base>
);
};
The async part of the action. The thunk part is as below. The error part of promise is giving an issue.
//thunk
export const signUpUser = (user) => {
return function (dispatch) {
dispatch(beginApiCall());
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
};
return axios
.post(config.API + 'signup', user, options)
.then((response) => {
debug('In success of axios');
dispatch(signUpUserSuccess(response.data));
})
.catch((err) => {
debug('In err of axios');
dispatch(signUpUserFailure(err.response.data));
dispatch(apiCallError());
});
};
};
In the reducer, I am returning a new object for failure cases(SIGNUP_USER_FAILURE) using the spread syntax to play by the immutability rules.
const authReducer = (state = initialState.auth, action) => {
switch (action.type) {
case types.LOAD_PROFILE:
if (!action.newAuth) {
action.newAuth = state;
}
return action.newAuth;
case types.SIGNUP_USER_SUCCESS:
if (!action.user) {
action.user = state;
}
debug(action.user);
return { ...action.user };
case types.SIGNUP_USER_FAILURE:
return { ...state, ...action.error };
default:
return state;
}
};
However, the state is updated in the reducer, but the component is not getting the updated state to take an action. But after the next rendering, the component is getting the data. I am thinking there is something going wrong with the rendering. Please help me here. I

Data from database displaying in the State but not on screen in React.js

I am new to react and designing an eCommerce website. In the admin side of the website, I have created a database to store customers information. I am able to write to the database and read from it (data is appearing in the state) but I am not able to display the data on screen. I have a function called 'displayCustomerList' to get the data from the state and format it but in the Render part, nothing is being displayed. Any help or advice is much appreciated.
import React, { Component } from "react";
import Title from "./Title";
import { Link, Redirect } from "react-router-dom";
import { ButtonContainer } from "./Button";
import axios from "axios";
export default class Admin extends Component {
constructor(props) {
super(props);
const token = localStorage.getItem("token");
let loggedIn = true;
if (token == null) {
loggedIn = false;
}
this.state = {
loggedIn,
name: "",
address: "",
postcode: "",
phone: "",
posts: [],
};
}
componentDidMount = () => {
this.getCustomerList();
};
getCustomerList = () => {
axios
.get("/api")
.then((response) => {
const data = response.data;
this.setState({ posts: data });
console.log("Data has been received");
})
.catch(() => {
alert("Error retrieving data");
});
};
handleChange = ({ target }) => {
const { name, value } = target;
this.setState({ [name]: value });
};
submit = (event) => {
event.preventDefault();
const payload = {
name: this.state.name,
address: this.state.address,
postcode: this.state.postcode,
phone: this.state.phone,
};
axios({
url: "/api/save",
method: "POST",
data: payload,
})
.then(() => {
console.log("Data has been sent to the server");
this.resetUserInputs();
this.getCustomerList();
})
.catch(() => {
console.log("Internal server error");
});
};
resetUserInputs = () => {
this.setState({
name: "",
address: "",
postcode: "",
phone: "",
});
};
//THIS IS CAUSING THE ISSUE
displayCustomerList = (posts) => {
if (!posts.length) return null;
console.log(posts) // I can see the array in the console
posts.map((post, index) => (
<div key={index} className="customer.list_display">
<h3>TEST</h3> //NOT DISPLAYING
<h3>{post.name}</h3>
<p>{post._id}</p>
<p>{post.address}</p>
<p>{post.postcode}</p>
<p>{post.phoneNumber}</p>
</div>
));
};
render() {
console.log("State: ", this.state);
if (this.state.loggedIn === false) {
return <Redirect to="/login" />;
}
return (
<React.Fragment>
<div className="py-5">
<div className="container">
<Title name="Admin" />
<Link to="/">Logout</Link>
</div>
</div>
<div className="card-footer d-flex justify-content-between">
<form onSubmit={this.submit} className="py-5">
<div className="form-input">
<input
type="text"
name="name"
placeholder="Name"
value={this.state.name}
onChange={this.handleChange}
className="nameInput"
/>
</div>
<div className="form-input">
<input
type="address"
name="address"
placeholder="Address"
value={this.state.address}
onChange={this.handleChange}
className="addressInput"
/>
</div>
<div className="form-input">
<input
type="text"
name="postcode"
placeholder="Postcode"
value={this.state.postcode}
onChange={this.handleChange}
className="postcodeInput"
/>
</div>
<div className="form-input">
<input
type="text"
name="phone"
placeholder="Phone number"
value={this.state.phone}
onChange={this.handleChange}
className="phoneInput"
/>
</div>
<ButtonContainer>submit</ButtonContainer>
</form>
</div>
<div>
{/* <CustomerList /> */}
<Title name="Customer List" />
</div>
//NOTHING IS BEING DISPLAYED
<div className="blog-">
{this.displayCustomerList(this.state.posts)}
</div>
</React.Fragment>
);
}
}
you should return JSX object from displayCustomerList like this:
displayCustomerList = (posts) => {
if (!posts.length) return null;
console.log(posts) // I can see the array in the console
return posts.map((post, index) => (
<div key={index} className="customer.list_display">
<h3>TEST</h3> //NOT DISPLAYING
<h3>{post.name}</h3>
<p>{post._id}</p>
<p>{post.address}</p>
<p>{post.postcode}</p>
<p>{post.phoneNumber}</p>
</div>
));
};

How to write a generic method to handle multiple state changes in React

I'm building an exercise tracker app in React.
Right now, I'm building the CreateExercise component to submit a form, so I need to update the states of each value. In order to do so, I created methods to handle those changes (onChangeUsername, onChangeDescription, onChangeDuration etc...) but I don't really like to repeat methods like this.
How to write a more generic method to handle this task ?
class CreateExercise extends Component {
constructor(props) {
super(props);
this.state = {
username: '',
description: '',
duration: 0,
date: new Date(),
users: []
}
}
onChangeUsername = (e) => {
this.setState({
username: e.target.value
});
}
onChangeDescription = (e) => {
this.setState({
description: e.target.value
});
}
onChangeDuration = (e) => {
this.setState({
duration: e.target.value
});
}
onChangeDate = (date) => {
this.setState({
date: date
});
}
onSubmit = (e) => {
e.preventDefault();
const exercise = {
username: this.state.username,
description: this.state.description,
duration: this.state.duration,
date: this.state.date
}
console.log(exercise);
window.location = '/';
}
render() {
return(
<div>
<h3>Create New Exercise Log</h3>
<form onSubmit={ this.onSubmit }>
<div className='form-group'>
<label>Username:</label>
<select
ref='userInput'
required
className='form-control'
value={ this.state.username }
onChange={ this.onChangeUsername }
>
{ this.state.users.map((user) => (
<option key={user} value={user}>{user}</option>
))
}
</select>
</div>
<div className='form-group'>
<label>Description:</label>
<input
type='text'
required
className='form-control'
value={ this.state.description }
onChange={ this.onChangeDescription}
/>
</div>
<div className='form-group'>
<label>Duration:</label>
<input
type='text'
className='form-control'
value={ this.state.duration }
onChange={ this.onChangeDuration }
/>
</div>
<div className='form-group'>
<label>Date:</label>
<div>
<DatePicker
selected={ this.state.date }
onChange={ this.onChangeDate }
/>
</div>
</div>
<div className='form-groupe'>
<input
type='submit'
value='Create Exercise Log'
className='btn btn-primary'
/>
</div>
</form>
</div>
);
}
}
export default CreateExercise;
Using partial application, create a function in your component that takes a field name, and returns a function that sets the state:
onChangeValue = field => e => {
this.setState({
[field]: e.target.value
});
};
Usage:
onChangeUsername = onChangeValue('username');
onChangeDescription = onChangeValue('description');
onChangeDuration = onChangeValue('duration');
You extend the idea further to support the onChangeDate as well:
onChangeValue = (field, valueTransformer = e => e.target.value) => e => {
this.setState({
[field]: valueTransformer(e.target.value)
});
};
This doesn't change the other on functions, since the default is to get e.target.value. To use onChangeDate we can now change the valueTransformer:
onChangeDate = onChangeValue('date', v => v);
You can define name for the HTML element, and use that to set value:
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
corresponding JSX element:
<input
type="text"
name="description"
required
className="form-control"
value={this.state.description}
onChange={this.onChange}
/>

Categories