I do not know if this is possible, but I am trying to store persistent data in async storage, so whenever the array data in the storage key is fetched, that persistent data is always at the top of the array. Also, persistent data cannot be deleted. below is a sample code, and I have shown only the functions for better clarity.
componentWillMount() {
this.saveData();
}
componentDidMount() {
this.getDataSync();
}
getDataSync = async () => {
try {
const list = await AsyncStorage.getItem(LIST_STORAGE_KEY);
const parsedList = JSON.parse(list);
this.setState({
isDataReady: true,
list: parsedList || []
});
console.log(parsedList, this.state.list);
} catch (e) {
Alert.alert('Failed to load list.');
}
}
saveData = () => {
const data = [
{
firstname: 'John',
lastname: 'Doe',
image: null,
email: 'john#doe.com',
key: 0,
phone: '19191919191',
},
];
this.setState(
prevState => ({
list: [data, ...prevState.list]
}),
() => this.saveItems(this.state.list)
);
}
handleDelete(item) {
if (item.key === 0) {
Alert.alert('You cannot delete this user');
} else {
this.setState(({ list }) => ({
list: list.filter(o => o.key !== item.key)
}),
() => this.saveItems(this.state.list)
);
console.log('deleted: ', item);
}
}
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));
};
Add another property to the objects, you don't want to be deleted. Asyncstorage is persistent by default.
{
firstname: 'John',
lastname: 'Doe',
image: null,
email: 'john#doe.com',
key: 0,
phone: '19191919191',
delete: false, // won't be deleted
},
In your getDataSync sort the list by delete property.
getDataSync = async () => {
try {
const list = await AsyncStorage.getItem(LIST_STORAGE_KEY);
const parsedList = JSON.parse(list);
// items with .delete property set to false, will be at the top of the list
parsedList.sort((x, y) => (x.delete === y.delete ) ? 0 : x ? 1 : -1);
this.setState({
isDataReady: true,
list: parsedList || []
});
console.log(parsedList, this.state.list);
} catch (e) {
Alert.alert('Failed to load list.');
}
}
Related
So I am doing a form validation check and I have taken 'formErrors' and set the errors in this object. However it is initially {} and in my code I am checking for Object.keys(formErrors).length===0 which returns true for even {}
const [formValues, setFormValues] = useState(initialValues);
const [formErrors, setFormErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setFormValues({ ...formValues, [name]: value });
};
const url = '/collectdetails';
const handleSubmit = (e) => {
e.preventDefault();
setFormErrors(validate(formValues));
// setIsSubmit(true);
console.log(noErrors);
if (noErrors) {
const { fullName, phoneNumber, emailAddress, role, lookingFor, company } =
formValues;
const data = {
Name: fullName,
MobileNumber: phoneNumber,
Email: emailAddress,
Role: role,
LookingFor: lookingFor,
CompanyName: company,
};
getDetails(url, data).then((user) => {
const { Response } = user;
if (Response === 'OK') {
setCurrentUser(phoneNumber);
navigate('/');
}
});
}
};
useEffect(() => {
if (Object.keys(formErrors).length === 0) {
console.log(formErrors);
setNoErrors(true);
}
}, [formErrors]);
So When I submit the handleSubmit() method is run and it has 2 nested checks. The first one is for noErrors which is a bool state that checks if my object is empty. I have console logged it and it returns true when the component loads as the object is {} in the beginning. Is there any way for me to put a check so that I can see if there are some keys present in the object?
useEffect will run every time your formErrors object changes. This includes the first render.
It would probably be better for you to put your useEffect logic inside your submit handler. The formErrors state object just seems to function as a temporary store for you as you immediately call setNoErrors(true) if it is populated:
const [formValues, setFormValues] = useState(initialValues);
const handleChange = (e) => {
const { name, value } = e.target;
setFormValues({ ...formValues, [name]: value });
};
const url = '/collectdetails';
const handleSubmit = (e) => {
e.preventDefault();
// just store in a normal variable
const errors = validate(formValues);
// setIsSubmit(true);
console.log(noErrors); // this isn't defined in your code
// just check the errors object for keys
if (Object.keys(errors).length === 0) {
// errors object is empty
console.log(errors);
setNoErrors(true);
const { fullName, phoneNumber, emailAddress, role, lookingFor, company }
= formValues;
const data = {
Name: fullName,
MobileNumber: phoneNumber,
Email: emailAddress,
Role: role,
LookingFor: lookingFor,
CompanyName: company,
};
getDetails(url, data).then((user) => {
const { Response } = user;
if (Response === 'OK') {
setCurrentUser(phoneNumber);
navigate('/');
}
});
}
};
I want store a new group as an object into the groups entity in the store. Everything works perfectly but the new group is stored as an object not as a string. I am using Mockoon to mock an API and the data type is set to be application/json. Can someone explain to me what might be the possible cause of this behavior? I am quite new on using redux so some input would be really appreciated too.
Thank you
const dispatch = useDispatch();
const initialGroupState = {
id: null,
name: "",
description: "",
members: []
}
const [group, setGroup] = useState(initialGroupState)
const [submitted, setSubmitted] = useState(false);
const handleInputChange = event => {
const { name, value } = event.target;
setGroup({ ...group, [name]: value });
};
const saveGroup = (e) => {
e.preventDefault();
const {name, description} = group;
dispatch(createGroup(name, description))
.then(data => {
setGroup({
id: Math.floor(Math.random() * 10000),
name: data.name,
description: data.description,
});
setSubmitted(true);
})
.catch(e => {
console.log(e);
});
}
const newGroup = () => {
setSubmitted(false);
};
My reducer:
const initialState = []
function groupsReducer(groups = initialState, action) {
const { type, payload } = action;
console.log([...groups]);
switch (type) {
case CREATE_GROUP:
return [...groups, payload];
case RETRIEVE_GROUPS:
return payload;
default:
return groups;
}
};
My actions:
export const createGroup = (name, description) => async (dispatch) => {
try {
const res = await GroupDataService.create({ name, description });
dispatch({
type: CREATE_GROUP,
payload: res.data,
});
console.log(res.data)
return Promise.resolve(res.data);
} catch (err) {
console.log(err)
return Promise.reject(err);
}
};
I have these codes to show all of the pending orders in a table. Once the button "confirmed" was clicked, the state will be updated where only the pending orders will be loaded. The only problem that I have is that, the state won't be updated not unless I'll go to another page and then I have to go back to the pending orders page to view the updated state.
class PendingOrders extends Component {
constructor() {
super();
this.state = { orders: [] };
}
columns = [
"Order ID",
{
name: "Confirm",
options: {
filter: true,
sort: false,
empty: true,
customBodyRender: (
value,
tableMeta,
) => {
return (
<FormControlLabel
value={value}
control={
<Button value={value} color="secondary" variant="primary">
confirm
</Button>
}
onClick={(e) => {
try {
firestore.collection("orders").doc(tableMeta.rowData[0]).set(
{
orderStatus: "Confirmed",
},
{ merge: true }
);
} catch (err) {
console.log(err);
}
}}
/>
);
},
},
},
componentDidMount() {
try {
firestore
.collection("orders")
.where("orderStatus", "==", "Pending")
.get()
.then((snapshot) => {
const orders = [];
snapshot.docs.map((doc) => {
const items = [];
orders.push({
const items = [];
doc.data().items.forEach((item) => {
items.push(`${item.productName}(${item.qty}),`);
});
const data = doc.data();
orders.push({
"Order ID": doc.id,
"Items":items,
});
});
this.setState({ orders: orders });
// console.log(this.state.orders);
});
} catch (err) {
console.log(err);
}
}
render() {
return (
<div>
<MUIDataTable
title={"Pending Orders"}
columns={this.columns}
data={this.state.orders}
options={this.options}
/>
</div>
);
}
}
I found the error already. Instead of using the .get() and .then(), I changed it into onSnapshot and it worked already.
componentDidMount() {
try {
firestore
.collection("orders")
.where("orderStatus", "==", "Pending")
.onSnapshot((snapshot) => {
const orders = [];
snapshot.docs.map((doc) => {
const items = [];
orders.push({
const items = [];
doc.data().items.forEach((item) => {
items.push(`${item.productName}(${item.qty}),`);
});
const data = doc.data();
orders.push({
"Order ID": doc.id,
"Items":items,
});
});
this.setState({ orders: orders });
// console.log(this.state.orders);
});
} catch (err) {
console.log(err);
}
}
You are not appending the data to your new order array. Your code:
snapshot.docs.map((doc) => {
const items = [];
const data = doc.data();
orders.push({
});
});
What should be:
snapshot.docs.map((doc) => {
const items = [];
const data = doc.data();
// Append data to the array, or the right keys if you don't want the whole data
orders.push(data);
});
You are suppose to update the state directly...
this.setState(prevState => {orders: [...prevState.orders, data]})
I was using graphql mutations like this and the .then & .catch work perfectly:
let submitForm = (
email: string,
firstName: string
) => {
setIsSubmitted(true);
if (email && (firstName)) {
const input: UpdateUserInput = {};
if (firstName) {
input.firstName = firstName;
}
updateUser({
variables: {
email: email,
input: input,
},
})
.then(({ data }: ExecutionResult<UpdateUserResponse>) => {
if (data !== null && data !== undefined) {
setIsUpdated(true);
}
})
.catch((error: { message: string }) => {
console.log('Error msg:' + error.message);
});
}
};
Now I am doing something similar here for a graphql query (fuller working version below):
let ShowUsers = () => {
const where: WhereInput = {};
if (criteria === '2') {
if (searchItem) {
where.firstName_contains = searchItem;
loadUsers({
variables: {
where: where
},
})
.then(({ data }: any) => {
if (data !== null && data !== undefined) {
}
})
}
}
}
but I keep getting an error on then that Property 'then' does not exist on type 'void'
Edit:
Without the .then, .catch, my code works correctly. Full form is something like this:
function UserSearchPage() {
const [criteria, setCriteria] = useState('');
const [searchItem, setSearchItem] = useState('');
const [loadUsers, { loading, data }] = useLazyQuery(LoadUsersQuery);
function PrintUsers({ data }: any) {
return (
<div>
{data &&
data.users.nodes &&
data.users.nodes.map((c: any, i: any) => (
<li key={i}>
Id: {c.id}, First Name: {c.firstName}, Last Name: {c.lastName},
Email: {c.email}, phoneNumber: {c.phoneNumber}
</li>
))}
</div>
);
}
let ShowUsers = () => {
const where: WhereInput = {};
if (criteria === '1') {
loadUsers({
variables: {
where: where
},
});
}
if (criteria === '2') {
if (searchItem) {
where.firstName_contains = searchItem;
loadUsers({
variables: {
where: where
},
});
}
}
};
return (
.....);
}
This is how the GraphQL query itself looks like:
interface UserFilter {
email_contains: String;
firstName_contains?: String;
lastName_contains?: String;
phoneNumber_contains?: String;
id?: Number;
}
export const LoadUsersQuery = gql`
query usersList($where: UserFilter) {
users(where: $where) {
nodes {
id
email
}
totalCount
}
}
`;
How else can I access the data properties/errors?
From the console.log, I know that this is returned:
Object
__typename: "User"
email: "first#first.com"
firstName: "First"
id: 148
lastName: "User"
phoneNumber: "+49123"
But if I try to access lets say data.users.id, why do I get undefined? How can I fix this?
As stated in other answers, it's known problem - "useLazyQuery execution function should return a promise #3499"
Instead of
loadUsers({
variables: {
where: where
},
})
.then(({ data }: any) => {
if (data !== null && data !== undefined) {
}
})
you can use onCompleted option
const [loadUsers, { loading, data }] = useLazyQuery(LoadUsersQuery, {
onCompleted: ( data : any ) => {
if (data !== null && data !== undefined) {
// some action
}
}
});
It depends on what exactly is happening in loadUsers, but it's likely that you've forgotten to a return statement there.
If you change loadUsers to return the promise for the user's it's loading, your code should start working nicely.
If you look at the docs for useLazyQuery, it does not return a Promise like useMutation so they behave differently.
Instead of relying on a Promise, you must utilize the second parameters (loading, data) returned when invoking useLazyQuery. This is why in your edit, your code works without the .then.
I have routes like that:
router.get('/:projectid/, (req, res) => {
testCase.getTestCaseDetail(req.params.projectid, req.params.testcaseid, req.params.snapshotId).then(testcaseData => {
res.render('testCaseService', {
title: 'Page',
testcase: testcaseData,
layout: 'project_layout',
});
});
});
In the handler function, I have getTestCaseDetail function:
function getTestCaseDetail(projectId, id, snapshotId) {
let testCaseId = parseInt(id);
return new Promise(((resolve, reject) => {
return testCaseSchema.aggregate([
{ $match: { 'projectId': projectId, 'testCaseId': testCaseId } },
{
$lookup: {
from: snapshotInfoSchema.collection.collectionName,
localField: testCaseObj.SERVICE_ID,
foreignField: 'artifacts.id',
as: 'services',
},
},
{ $unwind: '$services' },
{
$match: {
'services.snapshot.id': snapshotId,
}
}
]).then(testCaseResult => {
resolve(addTestCasesV2(testCaseResult, snapshotId));
})
.catch(err => {
reject(err);
})
}));
}
and addTestCasesV2 function
const addTestCasesV2 = function (testcases, snapshotId) {
const result = [];
let serviceTypeMapping;
let serviceName;
let testCase = {
id: '',
testCaseId: '',
name: '',
serviceName: '',
serviceType: '',
modifiedAt: '',
testScripts: '',
snapshotId: '',
services: '',
inputs: [],
outputs: [],
};
let promiseInputResults, promiseOutputResults;
const testcasesList = lodash.map(testcases, (tc) => {
const artifacts = lodash.map(tc.services.artifacts, (art) => {
if (art.id === tc.service_id) {
serviceTypeMapping = art.processType.serviceTypeName;
serviceName = art.name;
if (!commonUtil.isUndefined(art.processParameters)) {
if (!commonUtil.isUndefined(art.processParameters.input)) {
promiseInputResults = lodash.map(art.processParameters.input, (ip) => {
let classId = commonUtil.getArtifactId(ip.classId);
return objectType.getObjectTypeByClassId(snapshotId, classId)
});
}
if (!commonUtil.isUndefined(art.processParameters.output)) {
promiseOutputResults = lodash.map(art.processParameters.output, (ip) => {
let classId = commonUtil.getArtifactId(ip.classId);
return objectType.getObjectTypeByClassId(snapshotId, classId)
});
}
}
testCase.id = tc.testCaseId;
testCase.testCaseId = tc.testCaseId;
testCase.name = tc.name;
testCase.serviceName = serviceName;
testCase.serviceType = serviceTypeMapping;
testCase.modifiedAt = tc.modifiedAt;
testCase.testScripts = tc.testScripts;
testCase.snapshotId = snapshotId;
testCase.services = tc.services;
Promise.all(promiseInputResults).then(inputItems => {
return testCase.inputs = inputItems;
});
Promise.all(promiseOutputResults).then(outputItems => {
return testCase.outputs = outputItems;
});
}
});
});
return testCase;
};
The inputs/outputs is an list of item, like that:
inputs:[
{
name: "test1",
type: "String"
},
{
name: "test2",
type: "number"
},
]
I have a problem with promise lifecycle, this is the current flow
1. Routes
2. function getTestCaseDetail
3. resolve(addTestCasesV2(testCaseResult, snapshotId));
4. addTestCasesV2 ==> return testCase but without go to 2 promise.all functions
5. resolve(addTestCasesV2(testCaseResult, snapshotId));
6. Routes
7. go back 2 promise.all functions
8. end at return testCase.outputs = outputItems;
Please see the image to more detail flow (the white number is current flow, the orange number is my expect flow)
Please advice me. Many thanks.
Your code doesn't seem correct. If testcases is an array with more than one item, your lodash.map callback will be called testcases.length time. Each time overwriting testCase.id assigned in previous callback.
Anyways, I have corrected bits of your code to make it in run order that you wanted. I have logged ==step== at various places for your help.
First Function:
function getTestCaseDetail(projectId, id, snapshotId) {
let testCaseId = parseInt(id);
return new Promise(((resolve, reject) => {
return testCaseSchema.aggregate([
{ $match: { 'projectId': projectId, 'testCaseId': testCaseId } },
{
$lookup: {
from: snapshotInfoSchema.collection.collectionName,
localField: testCaseObj.SERVICE_ID,
foreignField: 'artifacts.id',
as: 'services',
},
},
{ $unwind: '$services' },
{
$match: {
'services.snapshot.id': snapshotId,
}
}
]).then(testCaseResult => {
console.log('=======STEP 1=======');
resolve(addTestCasesV2(testCaseResult, snapshotId));//=======STEP 2=======
console.log('=======STEP 5=======')
})
.catch(err => {
reject(err);
})
}));
}
Second function
const addTestCasesV2 = function (testcases, snapshotId) {
console.log('=======STEP 2=======')
const result = [];
let serviceTypeMapping;
let serviceName;
let testCase = {
id: '',
testCaseId: '',
name: '',
serviceName: '',
serviceType: '',
modifiedAt: '',
testScripts: '',
snapshotId: '',
services: '',
inputs: [],
outputs: [],
};
let promiseInputResults, promiseOutputResults;
return Promise.resolve()
.then(()=>{
console.log('=======STEP 3=======');
const testcasesList = lodash.map(testcases, (tc) => {
const artifacts = lodash.map(tc.services.artifacts, (art) => {
if (art.id === tc.service_id) {
serviceTypeMapping = art.processType.serviceTypeName;
serviceName = art.name;
if (!commonUtil.isUndefined(art.processParameters)) {
if (!commonUtil.isUndefined(art.processParameters.input)) {
promiseInputResults = lodash.map(art.processParameters.input, (ip) => {
let classId = commonUtil.getArtifactId(ip.classId);
return objectType.getObjectTypeByClassId(snapshotId, classId)
});
}
if (!commonUtil.isUndefined(art.processParameters.output)) {
promiseOutputResults = lodash.map(art.processParameters.output, (ip) => {
let classId = commonUtil.getArtifactId(ip.classId);
return objectType.getObjectTypeByClassId(snapshotId, classId)
});
}
}
testCase.id = tc.testCaseId;
testCase.testCaseId = tc.testCaseId;
testCase.name = tc.name;
testCase.serviceName = serviceName;
testCase.serviceType = serviceTypeMapping;
testCase.modifiedAt = tc.modifiedAt;
testCase.testScripts = tc.testScripts;
testCase.snapshotId = snapshotId;
testCase.services = tc.services;
/*=======FOLLOWING IS NOT REQUIRED=======*/
// Promise.all([promiseOutputResults]).then(outputItems => {
// return testCase.outputs = outputItems;
// });
}
});
});
return Promise.all([promiseInputResults,promiseOutputResults]);
})
.then(inputItems => {//array of resolved values
console.log('=======STEP 4=======');
testCase.inputs = inputItems[0];
testCase.outputs = inputItems[1];
return testCase;
})
};
Now you can use following to extract testcase from first function:
getTestCaseDetail(myProjectId, id, mySnapshotId)
.then(testCase=>console.log(testCase))
JSfiddle for your understanding.