I am using React for my FrontEnd.
I am using functional components. I am using Axios for fetching from an API.
In file GetAllSuppliers.js, I have the following:
function GetAllSuppliers(){
const [supplier, setSupplier] = useState([]);
useEffect(() => {
return axios.get(`http://localhost:8080/api/suppliers/supplier/list`)
.then((response) =>{
setSupplier((prevState) =>({ ...prevState,
id: response.data.id ,
supplierTitle: response.data.supplierTitle,
supplierFirstName: response.data.supplierFirstName,
supplierLastName: response.data.supplierLastName,
companyName: response.data.companyName,
phoneNumber: response.data.phoneNumber,
otherPhoneNumber: response.data.otherPhoneNumber,
accountNumber: response.data.accountNumber,
email: response.data.email,
address: response.data.address,
website: response.data.website,
hourlyRate: response.data.hourlyRate,
typeOfGoods: response.data.typeOfGoods,
paymentTerms: response.data.paymentTerms,
createdAt: response.data.createdAt,
notes: response.data.notes,
products: response.data.products,
components: response.data.components
}));
}).catch((error) =>{
setSupplier(error);
})
});
}
//other functions
export { GetAllSuppliers, other functions .... };
In FileB.js, I have the following code:
{GetAllSuppliers.supplier.map(t => <TableRow key={`supplier-${t.id}`} {...t} />)}
I am getting the following error:
TypeError: Cannot read property 'map' of undefined
What is a possible fix to the above error?
This happens when you're running map() on something that's undefined. 90% of the time, that means you're not handling the case that an axios call is in-progress and your variable isn't ready, yet (probably GetAllSuppliers, in this context).
Put in a conditional return that handles this case.
In your setSupplier you are setting an object but you need to return an array to map.
Something like this:
setSupplier((prevState) => {
return [
...prevState,
{
id: response.data.id,
supplierTitle: response.data.supplierTitle,
supplierFirstName: response.data.supplierFirstName,
supplierLastName: response.data.supplierLastName,
companyName: response.data.companyName,
phoneNumber: response.data.phoneNumber,
otherPhoneNumber: response.data.otherPhoneNumber,
accountNumber: response.data.accountNumber,
email: response.data.email,
address: response.data.address,
website: response.data.website,
hourlyRate: response.data.hourlyRate,
typeOfGoods: response.data.typeOfGoods,
paymentTerms: response.data.paymentTerms,
createdAt: response.data.createdAt,
notes: response.data.notes,
products: response.data.products,
components: response.data.components,
},
];
});
```
Related
I'm working on an application that involves a somewhat complex user registration. Something similar to this.
const [data, setData] = useState({
identity: '',
people: [{
name: '',
address: [{
street: '',
city: ''
}]
}]
})
function addAddress(){
setData({
...data,
people: [
...data.people,
{
address: [
...data.people[0].address,
{
street: '',
city: ''
}
]
}
]
})
}
When a user adds a new address to a person he is registering, he should add a new address to the person and keep the previous data. But it creates a new data array with just the address data, outside of the specified person.
Could someone help me how to do this insertion into the array?
It's not the best solution i guess, but it should work
I'm using here JSON.stringify() and JSON.parse() to deep copy your previous data
function addAddress (newAddress) => {
setData((previousData) => {
let newData = JSON.stringify(previousData);
newData=JSON.parse(newData);
newData.people[0].address.push(newAddress);
return (newData);
});
}
I have my Application in React JS. I am fetching data from an API at http://localhost:8080/api/list:
Here is the data that I am getting from the REST API in the Google Chrome console:
0:{
id: 4
supplierFirstname: "Tom"
supplierLastName: "ABC"
supplierTitle: "TomTheSupplier"
accountNumber: 1122234444
address: "111 ADrive, 1234 ST."
companyName: "TheTom Company & Associates"
email: "tomtomjayjay#email.com"
hourlyRate: 29
phoneNumber: 123456789
otherPhoneNumber: 1023456789
paymentTerms: "Credit"
notes: "Some Supplier"
createdAt: null
typeOfGoods: "Supplies"
website: "www.abc_123.com"
products: [{…}]
components:
[
0: {id: 5, name: "AComponent", unit: null, quantity: 0, componentCost: 0, …}
]
}
Here is my React code:
class SupplierData extends React.Component {
constructor(props) {
super(props);
this.state = {
supplier: [
{
id: 0,
supplierTitle: "Supplier Title",
supplierFirstName: "First Name",
supplierLastName: "Last Name",
companyName: "Company Name",
phoneNumber: "Phone Number",
otherPhoneNumber: "Phone Number (Other)",
accountNumber: "Account Number",
email: "Email",
address: "Address",
website: "Website",
hourlyRate: "Hourly Rate",
typeOfGoods: "Type Of Goods",
paymentTerms: "Payment Terms",
createdAt: "Created At",
notes: "Notes",
products: "Products",
components: "Components",
},
],
errorMessage: [],
};
this.ListAllSuppliers = this.ListAllSuppliers.bind(this);
}
ListAllSuppliers = async () => {
return await axios
.get(`http://localhost:8080/api/suppliers/supplier/list`)
.then((response) => {
let apiResults = response.data;
console.log(apiResults);
this.setState({ supplier: apiResults }); <--- The error happens here.
})
.catch((error) => {
this.setState({ errorMessage: this.state.errorMessage.push(error) });
});
};
componentDidMount() {
this.ListAllSuppliers();
}
}
export default SupplierData;
The problem that I am facing is in the React State. I am getting the following error:
Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might
indicate a bug in your application. Instead, assign to `this.state` directly or define a `state =
{};` class property with the desired state in the SupplierData component.
Question:
I want to set the state.
What is a possible fix the above error?
The code itself is mostly fine, though you're mixing calls to .then and .catch with async/await which is not best practice, and you're using bind unnecessarily on an arrow function.
It sounds like your component is getting unmounted after you've started your axios call and before it returns, which results in your calling setState on a component that isn't mounted. (Although the wording of the error is slightly different from the one I've seen.)
To avoid that, you can cancel the axios call from componentWillUnmount by passing a cancelToken in the options, and using the cancel method on its source from componentWillUnmount. (The built-in fetch offers the same functionality via AbortController.) (If you were doing something that didn't support cancellation, as a fallback, you could set this.unmounted in componentWillUnmount and then check that and not call setState if you see that this.unmounted is true.)
There are a few other issues with the code; here's an updated version with the cancel token mentioned above and other issues addressed inline (see comments):
class SupplierData extends React.Component{
constructor(props){
super(props);
this.state = {
supplier: [
{
id: 0,
supplierTitle: "Supplier Title",
supplierFirstName: "First Name",
supplierLastName: "Last Name",
companyName: "Company Name",
phoneNumber: "Phone Number",
otherPhoneNumber: "Phone Number (Other)",
accountNumber: "Account Number",
email: "Email",
address: "Address",
website: "Website",
hourlyRate: "Hourly Rate",
typeOfGoods: "Type Of Goods",
paymentTerms: "Payment Terms",
createdAt: "Created At",
notes: "Notes",
products: "Products",
components: "Components"
},
],
errorMessage: [],
};
// No need to bind `listAllSuppliers`, you don't use it as a callback
// A single cancel source that can cancel multiple operations
this.cancelSource = axios.CancelToken.source();
}
// Use an async method
// Note the initial lower case `l` (not `L`), which is standard in JavaScript
async listAllSuppliers() {
// In an `async` function, you use `try`/`catch` and `await`, not
// `.then` / `.catch` methods
try {
const response = await axios.get(
`http://localhost:8080/api/suppliers/supplier/list`,
{cancelToken: this.cancelSource.token}
);
const supplier = response.data;
console.log(supplier)
this.setState({supplier});
} catch (error) {
// Updating state based on existing state requires the callback form
// of `setState` , and you must never directly modify state objects
this.setState(({errorMessage}) => ({
errorMessage: [...errorMessage, error]
}));
}
}
componentDidMount() {
this.listAllSuppliers();
}
componentWillUnmount() {
// Cancel any outstanding axios calls
this.cancelSource.cancel("Component unmounted");
}
// Presumably you do have a `render` method, you'd get a different React error if you didn't
render() {
// ...
}
}
export default SupplierData;
I'm still learning about Node.js & MongoDB so my question is:
on Update functionality would like to update some of the fields not necessary all on them So How i can do that ?
Note: i used the same validateTask function for insert new data to tasks document.
router.put('/:id', (req, res) => {
const { error } = validateTask(req.body);
if(error) return res.status(400).send(error.details[0].message);
Task.findByIdAndUpdate(req.params.id, {
name: req.body.name,
employee: req.body.employee,
description: req.body.description,
section: req.body.section,
status: req.body.status,
updated_at: new Date()
}, { new: true })
.then ( data => {
res.status(201).send(data);
}).catch(err => {
res.status(422).send('The task with given ID was not found');
});
});
function validateTask(task) {
const schema = {
name: Joi.string().min(3).required(),
employee: Joi.string().min(3).required(),
description: Joi.string().min(3).required(),
section: Joi.string().min(3).required(),
status: Joi.boolean()
};
return Joi.validate(task, schema);
}
Remove the validateTask validation
The error is because in validateTask function you have made all the fields as required and these paramters are validated before findByIdAndUpdate.
I believe You need not have any required(mandatory field) validation to be done in update. This validation can be only done for documents that are newly inserted
For that, you can use Task.findByIdAndUpdate it will only update the fields you give to for example
User: {
name: Ahmed Gamal
email: ahmed#ahmedgamal.ga
country: Egypt
}
When used with findByIdAndUpdate and given {name: Ahmed} the result will be
User: {
name: Ahmed
email: ahmed#ahmedgamal.ga
country: Egypt
}
Getting the error TypeError: Converting circular structure to JSON on an object which doesn't have circular references.
I have tested the object in the console and had it stringified without a problem.
Even when I console log the object before passing it into the POST request, I get it stringified without a problem.
So I don't understand that on my Node server it throws this error when it gets to placing it into the request.
I have tried using npm packages flatted and yarn add json-stringify-safe, neither of which has helped.
Could it perhaps be caused by the request itself?
Here is the file contents:
const { inventorysource: { API_channel_ID, API_channel_ID_sandbox } } = require("../../config")
const post_order = async (instance, is_production, amount, user_data, res) => {
const assemble_data = {
order: {
order_number: user_data.new_id,
reference_number: user_data.new_id,
ordered_at: String(new Date()),
total_sale_price: amount,
taxes: (amount / (100 + user_data.taxes)) * 100,
notes: `${user_data.first_name} ${user_data.last_name}`,
shipping: {
method: user_data.shipping_info.name,
address: {
name: `${user_data.first_name} ${user_data.last_name}`,
company: user_data.company,
phone: user_data.phone,
email: user_data.email,
address: user_data.address_first,
address2: user_data.address_second,
city: user_data.city,
state: user_data.state,
zip: user_data.zip,
country: user_data.country
}
},
billing: {
address: {
name: `${user_data.billing_data.first_name} ${user_data.billing_data.last_name}`,
company: user_data.billing_data.company,
phone: user_data.billing_data.phone,
email: user_data.billing_data.email,
address: user_data.billing_data.address_first,
address2: user_data.billing_data.address_second,
city: user_data.billing_data.city,
state: user_data.billing_data.state,
zip: user_data.billing_data.zip,
country: user_data.billing_data.country
}
},
dealer: null,
items: user_data.products_data
}
}
const axios_instance = await instance
const stringigied_data = JSON.stringify(assemble_data)
try {
const { data } = await axios_instance.post(
`/channels/${ is_production ? API_channel_ID : API_channel_ID_sandbox }/orders`,
stringigied_data
)
res.send({
data: user_data,
order_data: data,
order_id: user_data.new_id
})
} catch(err) {
res.send(err)
console.log(err)
}
}
module.exports = post_order
I have also tried commenting out the items: user_data.products_data to check if it might be causing it, but still the error persist.
I would expect the POST request to go through without a problem.
I think it trying to convert a JSON object that is already a JSON.
I will try to check from here up:
JSON.stringify(assemble_data)
From last couple of hours, I'm trying to set simple array but somehow its not happening.
messages : [{message: string, nickname: string, user_id: string, profile_url: string, created_at: string, type: string}];
loadMessages(channelUrl){
this.getChannel(channelUrl)
.then(channel => {
this.channel = channel;
this.getMessageList(this.channel)
.then(messageList => {
this.messageList = messageList;
console.log(this.messageList);
this.messageList.forEach((messageData)=>{
console.log(messageData.message);
this.messages.push({message: messageData.message, nickname: '', user_id: '', profile_url: '', created_at: '', type: ''});
console.log(this.messages);
});
})
.catch(error => {
return error.message;
});
})
}
last console.log is not getting printed. neither its giving any errors.
Please guide.
Champagne has helped me to find the solution. I have added try catch because of that error was not getting displayed.
this.messages
was undefined