React "undefined" when posting with axios - reactjs

Hello im trying to push some data to my API with axios and react but i keep getting undefined. i cant find my error what am i doing wrong would appreciate some help.
im using 2 components one to handle the inputs which seem to work i can see the fields filled with data
import { useState } from 'react'
export const useForm = (initialValues) => {
const [values, setValues] = useState(initialValues);
return [
values,
e => {
setValues({
...values,
[e.target.name]: e.target.values
});
}
]
}
and the component where do my API CALL
import "../Admin/admin.css";
import Axios from "axios";
import { useForm } from "./useForm";
// Displays booking with a person added.
const AddBookableTime = () => {
const [values, handleChange] = useForm({ startTime: '', endTime: '' })
const API_ENDPOINT = "https://localhost:44387/api/BookableHours";
const PostBookingTimes = async (e) => {
e.preventDefault()
let times = createBookingTimes(e);
try {
await Axios.post(`${API_ENDPOINT}`, times);
} catch (err) {
}
};
//Creating object to send to api
const createBookingTimes = (e) => {
let bookableHours = {
startTime: e.target.values.startTime,
endTime: e.target.values.endTime,
};
return bookableHours;
};
return (
<>
<form onSubmit={(e) => PostBookingTimes(e)}>
<input type="text" name="startTime" value={values.startTime} onChange={handleChange} ></input>
<input type="text" name="endTime" value={values.endTime} onChange={handleChange} ></input>
<button type="submit" value="Post" className="booking-button">
Lägg till ny bokningsbar tid
</button>
</form>
</>
);
}
export default AddBookableTime;

Related

Param values are undefined during axios file upload along with text upload using FormData

I'm building a simple books management application. In a particular page, I need to upload a book's details along with a picture of the book.
I'm using formData and axios to do this. In the same request, sending the optional image as well as the text inputs.
But on reading the text fields from the body in the server side, all of them are undefined.
How can I resolve this issue ?
addBooksForm.js
import { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import "./addbooksform.css";
import axios from "axios"
import { authContext } from "../../App";
const Addbooks = () => {
// eslint-disable-next-line
const [authDet, setAuthDet] = useContext(authContext);
const navigate = useNavigate()
const [values, setValues] = useState({
title: "",
author: "",
genreId: 1,
price: 0,
picture:null
});
const handleSubmit = async (e) => {
e.preventDefault();
let data = new FormData()
data.set("title", values.title)
data.set("author", values.author)
data.set("genreId", values.genreId)
data.set("price", values.price)
data.set("picture", values.picture)
console.log(values)
const response = await axios.post("http://localhost:5000/api/books/",data,
{
headers:{
Authorization:`Token ${authDet.accessToken}`
}
})
if (response.status === 200) {
navigate('/yourbooks');
} else {
console.log("Error occurred "+ response)
}
};
const onChange = (e) => {
setValues({ ...values, [e.target.name]: e.target.value });
};
const onFileChange = (e) => {
setValues({...values, [e.target.name] : e.target.files[0] })
}
return (
<div className="addbooks">
<form onSubmit={handleSubmit}>
<h3>Title</h3>
<input type="text" name="title" required={true} onChange={onChange} value={values.title}/>
<h3>Author</h3>
<input type="text" name="author" required={true} onChange={onChange} value={values.author}/>
<h3>Genre</h3>
<input type="number" name="genreId" required={true} onChange={onChange} value={values.genreId}/>
<h3>Price</h3>
<input type="number" name="price" required={true} onChange={onChange} value={values.price}/>
<h3>Upload picture</h3>
<input type="file" name="picture" onChange={onFileChange}/>
<button>Add</button>
</form>
</div>
);
};
export default Addbooks;
I have also tried adding content-type:multipart/form-data in the config
Server side controller:
const addBooks = (e) => {
const { title, author, price, genreId } = req.body;
// further processing
}
here, all the fields are undefined
server.js:
app.use(express.urlencoded({extended:true}))
app.use(express.json())
app.use(cors())
Any help is appreciated. thanks in advance !!

How to save values sent via post request in redux-toolkit async thunk

I'm making a react component which has two input fields.One have the key : type,another the key: range.The problem is that when i submit the data i dont know how to save it as an array or something,to stack more pairs of information,because i need to display a progress bar based on the information from the input field. Could you help me please?
Here is my Slice:
export const skillSlice = createSlice({
name: "skill",
initialState: {
name:'',
range:null
},
reducers: {
setSkill: (state, action) => {
console.log("action", action.payload);
state.name = action.payload?.name;
state.range = action.payload?.range;
}
}
});
export const addNewSkill = createAsyncThunk(
'skills/addNewSkill',
async (_,{rejectWithValue,dispatch}) =>{
try{
const response = await fetch('/api/skills',{
method:'POST',
headers:{
'Content-name' : 'application/json',
},
});
if(!response.ok){
throw new Error('Can\'t add skill. Server error')
}
const data = await response.json();
dispatch(setSkill(data))
}catch(error){
return rejectWithValue(error.message);
}
}
)
export const fetchSkills = createAsyncThunk(
'skills/fetchSkills',
async (_, {rejectWithValue}) => {
try{
const response = await fetch('/api/skills',{
method:'GET',
})
// console.log(response)
if(!response.ok){
throw new Error ('Server Error!');
}
const data = await response.json();
// console.log(data)
return data;
} catch(error){
return rejectWithValue(error.message);
}
}
);
const { setSkill } = skillSlice.actions;
export const selectSkill = (state) => state?.skill;
export default skillSlice.reducer;
And here is the component:
import React, { useState,useEffect } from 'react'
import { Formik, Form, useFormik } from 'formik'
import * as Yup from 'yup'
import FormikControl from '../Form/FormikControl'
import DisplayFormikState from '../Form/DisplayFormikState.js'
import { useDispatch, useSelector } from 'react-redux'
import { addNewSkill,fetchSkills,selectSkill } from '../../features/skills/skillSlice'
const Skills = () =>{
const dispatch = useDispatch();
const [skill, setSkills] = useState({
name: '',
range: null
});
useEffect(()=>{
dispatch(fetchSkills());
},[dispatch])
const userInfo = useSelector(selectSkill);
const skillList = useSelector(state => state.skillState)
const { status, error } = useSelector(state => state.skillState)
const handleChange = (e) => {
const { name, value } = e.target;
setSkills({ ...skill, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addNewSkill(skill));
};
const formik = useFormik({
// initialValues:{
// name: skill.name,
// range: skill.range
// },
validationSchema:Yup.object({
}),
})
return(
<>
<section id="skills">
<h1 className='SkillSection'>Skills</h1>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="type">Type</label>
<input
id='type'
name='name'
type='text'
placeholder='Enter skill name'
onChange={handleChange}
// value={formik.values.name}
/>
</div>
<div>
<label htmlFor="level">Level</label>
<input
id='level'
type='text'
name='range'
placeholder='Enter range'
onChange={handleChange}
// value={formik.values.range}
/>
</div>
<button type='submit'>Submit</button>
</form>
</section>
</>
)
}
export default Skills
In the above code the initial state isn't an array because when i tried to push values to it i got undefined,so,i left the working state not to get confused. Thanks in advance!

React Query: InvalidateQuery not working to update users list

I have a simple app that has a form and list. Currently, I am using query client.InvalidateQueries to update the users' list after submitting the form. As the documentation says, using InvalidateQuery will trigger refetching, but somehow I had not seen an update to the list after adding users. Am I missing something?
Add User
import React, { useState } from 'react';
import { useFormik } from 'formik';
import Input from '../../elements/Input/Input';
import * as Yup from 'yup';
import { QueryClient, useMutation } from 'react-query';
import axios from 'axios';
const queryClient = new QueryClient();
const CreateItemView = () => {
function gen4() {
return Math.random().toString(16).slice(-4);
}
function generateID(prefix) {
return (prefix || '').concat([gen4(), gen4(), gen4(), gen4(), gen4(), gen4(), gen4(), gen4()].join(''));
}
const mutation = useMutation(
(formData) => {
axios.post('http://localhost:8000/users', formData).then((response) => console.log(response));
},
{
onSuccess: () => {
queryClient.invalidateQueries('users');
},
},
);
const [data, setData] = useState([]);
const initialValues = {
id: '',
name: '',
email: '',
channel: '',
};
const onSubmit = (values, { resetForm }) => {
setData([...data, values]);
const ID = generateID().toString();
values.id = ID;
mutation.mutate(values);
resetForm();
};
const validationSchema = Yup.object({
name: Yup.string().required('Required!'),
email: Yup.string().email('Invalid format').required('Required!'),
channel: Yup.string().required('Required!'),
});
const formik = useFormik({
initialValues,
onSubmit,
validationSchema,
});
return (
<div>
<form onSubmit={formik.handleSubmit}>
<Input type={'text'} name={'name'} id={'name'} label={'Name'} formik={formik} />
<Input type={'email'} name={'email'} id={'email'} label={'Email'} formik={formik} />
<Input type={'text'} name={'channel'} id={'channel'} label={'channel'} formik={formik} />
<button type="submit">Submit</button>
</form>
</div>
);
};
export default CreateItemView;
User's list
import React from 'react';
import ListView from './ListView';
import { useQuery } from 'react-query';
import axios from 'axios';
const getUsers = async () => {
const response = await axios.get('http://localhost:8000/users');
return response.data;
};
const ListContainer = () => {
const { data, isLoading, isFetching } = useQuery('users', getUsers);
console.log('list', data);
return <div>{isFetching ? 'loading...' : <ListView dataSource={data} />}</div>;
};
export default ListContainer;
You have to return the fetch function in the mutation. The onSuccess handler will fire when the promise is resolved.
const mutation = useMutation(
formData => {
return axios.post('http://localhost:8000/users', formData)
.then((response) => console.log(response));
},
{
onSuccess: () => {
queryClient.invalidateQueries('users');
},
},
);
i think the solution for your problem is to replace this :
onSuccess: () => {
queryClient.invalidateQueries('users');
},
By this :
onSettled:() => {
queryClient.invalidateQueries('users');
},
you should use the same instance of queryClient from the root of your app which is accessible via the useQueryClient() hook.
Hence you should be doing const queryClient = useQueryClient() instead of generating new instance with const queryClient = new QueryClient().

React component loads non-stop

My React component loads infinitely and I want it to load only depending on the data that I get from the database, the console.log("1") is only for testing how many times the component loads.
This is the component:
import React from "react";
import Axios from "axios";
import { useState, useEffect } from "react";
function Added() {
const [data, setData] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/").then((result) => {
setData(result.data);
});
}, [data]);
console.log("1");
return data.map((item) => {
return (
<div key={item._id}>
<h1>{item.finame}</h1>
<h1>{item.laname}</h1>
<h5>{item.age}</h5>
</div>
);
});
}
export default Added;
This is where it loads:
import "./App.css";
import { useState, useReducer, useEffect } from "react";
import Added from "./added";
import Axios from "axios";
function App() {
const GettingALlTheData = () => {
return Axios.get("http://localhost:3001/").then((result) => {
return result.data;
});
};
/* -------------------- For the useReducer -------------------- */
const Actions = {
Add: "add",
};
const defaultState = {
list: [GettingALlTheData],
};
console.log(defaultState);
const reducer = (state, action) => {
switch (action.type) {
case Actions.Add:
const listItem = action.payload;
try {
Axios.post("http://localhost:3001/add", listItem);
} catch (error) {
console.log(error + "444444");
}
return { ...state, list: [...state.list, listItem] };
default:
console.log("this is the default");
}
};
const [state, dispatch] = useReducer(reducer, defaultState);
/* ---------------------------- For the form ---------------------------- */
const [listItem, setListItem] = useState({ finame: "", laname: "", age: 0 });
const [list, setList] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/").then((result) => {
state.list = result.data;
});
}, [state.list]);
const handelChange = (e) => {
const name = e.target.name;
const value = e.target.value;
setListItem({ ...listItem, [name]: value });
};
const handelSubmit = (e) => {
e.preventDefault();
dispatch({ type: Actions.Add, payload: listItem });
};
const [data, setData] = useState({});
/* -------- for the useEffect to get the Data from the server -------- */
/* ------------------------ for the form return ---------------------- */
return (
<div className="App">
<h1>CRUD app using MERN stack</h1>
<form onSubmit={handelSubmit}>
<label htmlFor="finame">First name:</label>
<input
type="text"
name="finame"
id="finame"
value={listItem.finame}
onChange={handelChange}
/>
<label htmlFor="laname">Last name:</label>
<input
type="text"
name="laname"
id="laname"
value={listItem.laname}
onChange={handelChange}
/>
<label htmlFor="age">Age:</label>
<input
type="Number"
name="age"
id="age"
value={listItem.age}
onChange={handelChange}
/>
<button type="Submit">Submit</button>
</form>
{state.list ? (
<Added />
) : (
state.list.map((listItem) => {
return (
<div key={listItem._id}>
<h1>First name : {listItem.finame}</h1>
<h1>Last name: {listItem.laname}</h1>
<h3>Age: {listItem.age}</h3>
</div>
);
})
)}
</div>
);
}
export default App;
That's because you use the useEffect function with no dependency, so it is executed every time any prop/state changes (it's like a class component's componentDidUpdate).
I suggest you use it inside your Added component like a componentDidMount, so that it only execute once. To do it, you have to pass an empty dependency array, like so:
useEffect(() => {
//fetching the data
}, []);

Test login component with react hooks,

I am trying to test the Login.js component of my app.
I am actually trying to test 3 things:
When the values in the inputs field are not filled the disabled prop is true.
When the values in the inputs field are filled the disabled prop is false.
When the values in the inputs field are filled the login-button should call handleSubmit.
My test is failing in the second test and regarding the third test, I am not sure how to approach this test.
If someone has a good idea what I am doing wrong will be appreciated.
//Login.js
import React, { useState } from 'react';
import axios from 'axios';
import useStyles from '../styles/LoginStyle';
import { useStatefulFields } from '../../hooks/useStatefulFields';
function Login({ success }) {
const [values, handleChange] = useStatefulFields();
const [error, setError] = useState();
const handleSubmit = async () => {
await axios.post('www.example.com', {values}, {key})
.then((res) => {
if (res.data.success) {
success();
} else {
setError(res.data.error);
}
})
};
return (
<div>
<p className={classes.p}>Personalnummer</p>
<input
type="number"
className={classes.input}
onChange={handleChange}
name="personal_number"
title="personal_number"
/>
<p className={classes.p}>Arbeitsplatz</p>
<input
type="number"
onChange={(e) => handleChange(e)}
name="workplace"
title="workplace"
className={classes.input}
/>
<ColorButton
id="login-button"
className={
(values.password && values.personal_number && values.workplace)
? classes.button
: classes.buttonGray
}
disabled={!values.password && !values.personal_number && !values.workplace}
size="large"
onClick={
values.password && values.personal_number && values.workplace
? handleSubmit
: () => {}
}
>
</ColorButton>
</div>
)
}
//useStatefulFields.js
import { useState } from 'react';
export function useStatefulFields() {
const [values, setValues] = useState({});
const handleChange = (e) => {
setValues({
...values,
[e.target.name]: e.target.value,
});
};
return [values, handleChange];
}
//Login.test.js
import React from 'react';
import { shallow } from 'enzyme';
import Login from '../components/Workers/Login';
let wrapper;
beforeEach(() => {
wrapper = shallow(<Login success={() => {}} />);
});
test('check if login button is disabled', () => {
const loginButton = wrapper.find('#login-button');
expect(loginButton.prop('disabled')).toEqual(true);
});
test('check if login button is not disabled', () => {
const loginButton = wrapper.find('#login-button');
wrapper.find('input[name="workplace"]').simulate('change', { target: { value: 'test' } });
wrapper.find('input[name="password"]').simulate('change', { target: { value: 'test' } });
wrapper.find('input[name="personal_number"]').simulate('change', { target: { value: 'test' } });
expect(loginButton.prop('disabled')).toEqual(false);
});
Pass name and value once simulate the change.
test('check if login button is not disabled', () => {
let loginButton = wrapper.find('#login-button');
wrapper.find('input[name="workplace"]').simulate('change', { target: { name:'workplace', value: 'test' } });
wrapper.find('input[name="password"]').simulate('change', { target: { name:'password', value: 'test' } });
wrapper.find('input[name="personal_number"]').simulate('change', { target: {name: 'personal_number', value: 'test' } });
loginButton = wrapper.find('#login-button');
loginButton.props().onClick(); // for handleSubmit
expect(loginButton.prop('disabled')).toEqual(false);
});
Make sure that you have the input field for the password also that I am not seeing in your question. Otherwise, the test will fail again.

Resources