Im not sure why my state wont update using errorOption = key + 'Error' and then setting state with [errorOption]: true. Because my formErrors state uses the same initial key as formDetails, but with 'Error' at the end so i just concatenated it. I've tried using template literals as-well, but concatenation looks easier to read here.
I want it to validate form to check for empty and change error state to true
const defaultData = {
username: '',
password: '',
}
const defaultErrorState = {
usernameError: false,
passwordError: false,
}
const [formDetails, setFormDetails] = useState(defaultData);
const [formErrors, setFormError] = useState(defaultErrorState);
const {
username,
password
} = formDetails
const {
usernameError,
passwordError
} = formErrors
const validateForm = (inputs) => {
const checkThreshold = (currentValue) => currentValue.length > 0;
let errorOption
Object.entries(inputs).forEach(([key, value]) => {
if (!checkThreshold(value)) {
errorOption = key + 'Error'
setFormError({
...formErrors,
[errorOption]: true
})
}
})
Object.entries(inputs).every(checkThreshold)
}
const handleSubmit = (event) => {
const basicInfoValues = {
username: username,
password: password,
}
if (validateForm(basicInfoValues)){
// login stuff
} else {alert("Fill in all fields")}
}
Related
So, I am trying to update category data using useMutation hook in react. The fields I am going to mutate are name, description with it's associated id. But whenever I send mutation I got Missing field 'editScat' while writing result true error. Here is my code.
const [categoryName, setCategoryName] = useState("");
const [categoryDescription, setCategoryDescription] = useState("");
const [categoryId, setCategoryId] = useState("");
const EDIT_SUB_CATEGORY = gql`
mutation editScat($id: UUID!, $name: String!, $description: String!) {
editScat(id: $id, name: $name, description: $description) {
payload {
id
name
description
}
}
}
`;
const [updateCatMutation] = useMutation(EDIT_SUB_CATEGORY);
const updateMe = (e) => {
e.preventDefault();
updateCatMutation({
variables: {
id: categoryId,
name: categoryName,
description: categoryDescription,
},
optimisticResponse: true,
})
.then((res) => {
alert("Success");
})
.catch((err) => {
alert("Failed");
});
};
What did I missed here?
My Code looks like this:
interface MutationProps{
username: any,
Mutation: any
}
const UseCustomMutation: React.FC<MutationProps> = (MutationProps: MutationProps) => {
const [myFunc, {data, error}] = useMutation(MutationProps.Mutation);
useEffect(() => {
myFunc({variables:{username: MutationProps.username}})
console.log(JSON.stringify(data))
console.log(JSON.stringify(error, null , 2))
}, [])
return data
}
export const DisplayUser = () => {
const GET_USER = gql`
mutation GetUser($username: String!){
getUser(username: $username) {
pfp
username
password
age
CurrentLive
ismod
description
fullname
}
}
`
const {username} : {username: any} = useParams()
const MyData = UseCustomMutation(username, GET_USER)
console.log(JSON.stringify(MyData))
But I get this error back: ×
Argument of undefined passed to parser was not a valid GraphQL DocumentNode. You may need to use >'graphql-tag' or another method to convert your operation into a document
How about your code looks like this:
interface MutationProps {
username: string;
Mutation: any;
}
const UseCustomMutation: React.FC<MutationProps> = ({ username, Mutation }) => {
const [functionForDoingAction, { data, loading, error }] = useMutation(
Mutation,
{
variables: {
username,
},
}
);
useEffect(() => {
// fn trigger for change data
functionForDoingAction({
variables: {
username: "string_value",
},
});
console.log(JSON.stringify(data));
console.log(JSON.stringify(error, null, 2));
}, []);
if (loading) return "loading...";
if (error) return `Submission error! ${error.message}`;
return data;
};
export const DisplayUser = () => {
const GET_USER = gql`
mutation GetUser($username: String!) {
getUser(username: $username) {
pfp
username
password
age
CurrentLive
ismod
description
fullname
}
}
`;
const { username }: { username: string } = useParams();
const MyData = UseCustomMutation(username, GET_USER);
console.log(JSON.stringify(MyData));
};
you can pass an argument directly to the useMutation hook which they provide as an Options parameter. Or is the direct trigger function from the hook you get.
I have a function that creates a new user, using name, email, password and role. It validates name and email, but it does not validate the passoword.
This is the function that creates a new user:
const createNewUser = async () => {
try {
const response = await axios.post("/user/create", {
name: newUser.newName,
email: newUser.newEmail,
password: newUser.newPassword,
role: newUser.newRole,
});
if (response.status === 200) {
fetchAllUsers(token).then((res) => {
dispatch(dispatchGetAllUsers(res));
});
setNewUser({
newName: "",
newEmail: "",
newPassword: "",
newRole: 0,
});
}
else {
setErrorNew(response.msg);
}
} catch (error) {
setErrorNew(error.response.data.msg);
}
};
And this is the function that returns error message if input is empty:
const handleChangeNew = (ev) => {
const { id, value } = ev.target;
setErrorNew("");
setNewUser({ ...newUser, [id]: value });
};
I would like to return a error messaje if password input length is less than 6 characters
Just do something like this:
const handleChangeNew = (ev) => {
const { id, value } = ev.target;
if(value.length < 6 && id === 'newPassword') {
setErrorNew("Your password should contain minimum of 6 characters");
} else {
setErrorNew("");
}
setNewUser({ ...newUser, [id]: value });
};
I want to store an array state using async storage. but everytime i reload the app, it comes up blank. below is a sample code, and I have shown only the functions for better clarity.
componentDidMount() {
this.getDataSync();
}
getDataSync = async () => {
try {
const list = await AsyncStorage.getItem(LIST_STORAGE_KEY);
const parsedList = JSON.parse(list);
const obj = Object.keys(parsedList);
this.setState({ isDataReady: true, list: obj || [] });
} catch (e) {
Alert.alert('Failed to load list.');
}
}
handleAdd() {
const { firstname, lastname, email, phone} = this.state;
const ID = uuid();
const newItemObject = {
key: ID,
firstname: firstname,
lastname: lastname,
email: email,
phone: phone,
image: null,
};
this.setState(prevState => ({
list: [...prevState.list, newItemObject]
}));
this.saveItems(this.state.list);
}
saveItems = list => {
AsyncStorage.setItem(LIST_STORAGE_KEY, JSON.stringify(list));
};
You are not saving your list but getting keys from the list. const obj = Object.keys(parsedList); you are saving array indexes to state.
getDataSync = async () => {
try {
const list = await AsyncStorage.getItem(LIST_STORAGE_KEY);
const parsedList = JSON.parse(list);
this.setState({
isDataReady: true,
list: Array.isArray(parsedList) && parsedList.length && parsedList || []
});
} catch (e) {
Alert.alert('Failed to load list.');
}
}
Also pass saveItems as a callback to save the correct data.
this.setState(prevState => ({
list: [...prevState.list, newItemObject]
}), () => this.saveItems(this.state.list));
The .setState() method is may be asynchronous, so the result of setting the state, cannot be used immediately after setting it. If you want to use the results of setting the state, you should use the callback (2nd param), which is called after the state is actually set:
this.setState(
prevState => ({
list: [...prevState.list, newItemObject]
}),
() => this.saveItems(this.state.list)
);
In my react component I have two functions. handleChangeInput(e) is called on 'OnChange' of input field and checkFields() is called from handleChangeInput(e)
constructor(){
super()
this.state={
email: '',
password:'',
validFields: false
}
}
handleChangeInput(e){
const name = e.target.name;
const value = e.target.value;
this.setState({[name]: value},()=>{
this.checkFields();
});
}
checkFields(){
if (this.state.email.length>0 && this.state.password.length>0 ) {
this.setState({validFields: true});
}else {
this.setState({validFields: false});
}
}
And in my index.test.js I have
describe('<Login />', () => {
describe('handleChangeInput', () => {
const component = new Login()
const wrapper = shallow(<Login />);
beforeEach(() => {
component.setState = jest.fn()
})
test('calls setState validFields false when no email/password', () => {
const state = { state : { email: '', password: ''} }
const args = { target : { name: 'name', value: 'value' } }
component.handleChangeInput.call(state, args)
expect(component.setState.mock.calls.length).toBe(1)
expect(wrapper.state().validFields).toEqual(false)
})
test('calls setState validFields true when email/password are ok', () => {
const state = { state : { email: 'email', password: 'password' } }
const args = { target : { name: 'name', value: 'value' } }
component.handleChangeInput.call(state, args)
expect(component.setState.mock.calls.length).toBe(1)
expect(wrapper.state().validFields).toEqual(false)
})
})
});
But my state is not being updated. As a result, 'validFields' is not set to true and my second test is failing. I tried wrapper.update() and wrapper.instance().forceUpdate() but still no success. Any help would be appreciated
I am guessing it might be because you override the setState function with jest.fn()
component.setState = jest.fn()
})
how about removing this?
hope my answer does not come too late, but you are trying to update the state in a wrong way.
First of all, remove these two:
const component = new Login()
beforeEach(() => {
component.setState = jest.fn()
})
And most likely you want to change this:
handleChangeInput(e){
const name = e.target.name;
const value = e.target.value;
this.setState({[name]: value},()=>{
this.checkFields();
});
}
handleChangeInput(e){
const name = e.target.name;
const value = e.target.value;
this.setState(()=>{
return { email: name}
});
this.setState(()=>{
return { password: value }
});
this.checkFields();
}
const component = new Login() does not bring any value to this test and you should not mock the setState if you want that it's actually changed.
Instead you should test the actual component (like you partially already do here)
Change the code like this:
test('calls setState validFields true when email/password are ok', () => {
const args = { target : { email: 'email', password: 'password' } }
wrapper.instance().handleChangeInput(args)
expect(wrapper.state('email')).toEqual('email')
expect(wrapper.state('password')).toEqual('password')
expect(wrapper.state('validFields')).toBeTruthy()
})
I found this answer in one of the git forums. It worked for me.
// somewhere in your test setup code
global.flushPromises = () => {
return new Promise(resolve => setImmediate(resolve))
}
test('something with unreachable promises', () => {
expect.hasAssertions()
const component = mount(<Something />)
// do something to your component here that waits for a promise to return
return flushPromises().then(() => {
component.update() // still may be needed depending on your implementation
expect(component.html()).toMatchSnapshot()
})
})