react js request result - node.js

Hello I have a problem getting a value and setting in my state in react
I can see data in console of my api response and everything goes well.
export default class index extends Component {
constructor(props){
super(props)
this.state={ products: [], filteredProducts:[]}
}
componentDidMount(){
api.get('/products').then( result => this.setState({
products: result.data.listProducts,
filteredProducts: result.data.listProducts
}))
console.log(this.state.products)
}
but when I console my state value, it appears an empty array
index.js:16 [] console.log(this.state
index.js:11 (5) [{…}, {…}, {…}, {…}, {…}] console.log( data request
Well I don't know if it's a problem with my back end
I made a map to filter what I will return to my front end since I have
an array of 3 objects
I don't know if I made the best option or if I can do better, if I can improve the code I would be happy if someone could alert me:
async getAllProduct(req,res){
try {
const results = await Products.findAll({
// raw: true, <= remove
attributes:['id','name', 'float', 'price'],
include: [{
model: SubCategory,
as: 'subcategory',
attributes: ['id','name'],
},
{
model:Exteriors,
as: 'exteriors',
attributes: ['id','name']
},
{
model:Types,
as: 'types',
attributes: ['id','name']
},
],
})
const listProducts = []
results.map(record =>
record.get({ plain: true }));
results.map( (products) => {
const model = {
id: products.id,
name: products.name,
float: products.float,
price: products.price,
id_sub: products.subcategory.id,
subcategory: products.subcategory.name,
id_types: products.types.id,
type: products.types.name,
id_ext: products.exteriors.id,
exterior: products.exteriors.name,
}
listProducts.push(model);
})
if(listProducts){return res.status(200).json({listProducts})}
else{return res.status(400).json({result: 'failed to get Products'})}
} catch (error) {
console.error(error);
}
}

setState is async, you can't see updated state right after setting the state,
You can have callback in setState to check the updated state,
this.setState({
products: result.data.listProducts,
filteredProducts: result.data.listProducts
}, () => console.log(this.state.products)) //callback method

If you console.log right after a state update you will log the old state. Try logging the state in componentDidUpdate to see if the state is actually empty:
componentDidUpdate() {
console.log(this.state)
}

Related

NodeJS & Sequelize: showing all the conversation the current user has

i am currently trying to implement my backend from my all conversations screen. i am finding all the conversations and including all the messages associated with each conversation.
In my react native frontend, in my all conversations screen, i am showing all the conversations as a flatlist and showing the last message sent between the 2 users by picking it out from the list of messages.
Should i continue using this approach or will it be better in terms of performance to include a link to the last message in Conversation table and update it every time a new message is sent in the conversation? If so, how do i change my code to do so?
Conversation Model:
const Conversation = db.define("Conversation", {},
{
paranoid: true,
timestamps: true,
deletedAt: "deletedAt",
}
);
module.exports = Conversation;
User.hasMany(Conversation, {
foreignKey: 'user1'
});
User.hasMany(Conversation, {
foreignKey: 'user2'
});
Conversation.belongsTo(User, { as: 'Creator', foreignKey: 'user1', allowNull: false })
Conversation.belongsTo(User, { as: 'Recipient', foreignKey: 'user2', allowNull: false })
Message Model:
const Message = db.define("Message", {
senderId: {
type: Sequelize.STRING,
},
receiverId: {
type: Sequelize.STRING,
},
message: {
type: Sequelize.STRING,
},
conversationId:{
type: Sequelize.INTEGER,
}
});
module.exports = Message;
User.hasMany(Message, {
foreignKey: 'senderId',
});
User.hasMany(Message, {
foreignKey: 'receiverId',
});
Message.associate = (models) => {
Message.belongsTo(models.Conversations,{
foreignKey: "conversationId",
}
);
};
Conversation.hasMany(Message,{
foreignKey: "conversationId",
}
);
Message.belongsTo(User, {
as:"Sender",
foreignKey: "senderId",
allowNull: false
});
Message.belongsTo(User, {
as:"Receiver",
foreignKey: "receiverId",
allowNull: false
});
Get All Conversations Query:
router.get('/', auth, async (req, res) => {
const conversations = await Conversation.findAll({
order: [[Message,"createdAt", "DESC"]],
include: [
{
model: Message,
where: {
[Op.or]: [
{ senderId: req.user.id },
{ receiverId: req.user.id },
],
},
required: true, // RIGHT JOIN
}]
})
if (!conversations) return res.status(404).send();
res.status(200).send();
});
You can try to limit included messages in each conversation to the last message only
const conversations = await Conversation.findAll({
order: [[Message,"createdAt", "DESC"]],
include: [
{
model: Message,
order: [["createdAt", "DESC"]],
limit: 1,
where: {
[Op.or]: [
{ senderId: req.user.id },
{ receiverId: req.user.id },
],
},
required: true, // RIGHT JOIN
}]
})
If the above query doesn't work you can always try to store the last message id in a conversation but this can lead to inconsistencies if you store a message and forget to update a link to the last message in a conversation or you delete the last message and again forget to update the link in a conversation.
You can cache the conversations and messages into the local database
using sqlite of the mobile device.
To do so, you don't have to query whole conversation for every login.
Meta data table can be even helpful to relieve the server stress
I suggest you to use sqlite in RN and add meta table in remote database.
Pattern.
Load Conversations from remote database with the count of messages when logining in.
( If meta data table exists, this part will be faster and lighter )
When displaying the conversions, use the lastest data in local database.
Comparing count of messages, if count is different, you can assume that user in this device didn't check the lastest message. So Render badge component to notify user that he or she didn't read it yet.
When user enter the conversation, fetch messages and store it into local database of the device.
Socket io part
Brief concept is like this.
ref. https://socket.io/docs/v3/server-api/#socket-on-eventName-callback
Client
const [ conversations, setConversations ] = useState([])
const [ syncState, setSyncState ] = useState(false)
const [ socket, setSocket ] = useState(null)
useEffect(() =>{
...
// you might wanna set conversation from local database here
...
io.on('connection', soc => {
soc.join("your room")
...
socket.on('synced', data =>{
//mapNewDataToConversationState
...
setSynced(true)
})
setSocket(soc)
})
...
}, [ ... ])
useEffect(() =>{//when loaded and socked established
if(!socket) return
if(syncState) return
socket.emit('sync', { data : conversation })
}, [ socket ])
Server
//let say you already set app.io as socket io connection
//add a event listener for client sync
socket.on('sync', async conversation =>{
const responseData = {}
for(const c of conversation){
const lastestMsg = c.Message[c.Message.length - 1]
const messages = await Message.findAll({
where : {
...//Maybe greater than createdAt, any cursor attribute in here
}
})
responseData[c.id] = messages
}
socket.emit('synced', responseData )
})

State is changing but transitions are not triggered in Xstate

I am working with xstate with Nextjs. Now I am stuck somewhere.
import { assign, createMachine, interpret } from "xstate";
export interface toggleAuth {
isAuthenticated: boolean;
user: {
name: string | undefined;
};
}
// console.log(getCachedData());
export const authMachine = createMachine<toggleAuth>({
id: "auth",
initial: "unauthenticated",
context: {
isAuthenticated: false,
user: {
name: undefined,
},
},
states: {
authenticated: {
on: {
toggle: {
target: "unauthenticated",
},
},
entry: assign({
user: (ctx) => (ctx.user = { name: "Pranta" }),
isAuthenticated: (ctx) => (ctx.isAuthenticated = true),
}),
},
unauthenticated: {
on: {
toggle: {
target: "authenticated",
},
},
entry: assign({
user: (ctx) => (ctx.user = { name: undefined }),
isAuthenticated: (ctx) => (ctx.isAuthenticated = false),
}),
},
},
});
const service = interpret(authMachine);
service.onTransition((state) => console.log(state));
So I was watching the docs. According to them, whenever I transition from unauthenticated to authenticated and authenticated to unauthenticated, it should console log it for me. But it doesn't. It does only one time. What's happening here. Also, is it okay to define my machine like this? Thanks in advance.
It's not logging because you're not changing state; no event is ever being sent.
Please re-read the documentation on assigning to context - you are mutating context instead of assigning new values; the assigners should always be pure.
If you want to see the state change, you need to send a toggle event in this case:
service.send('toggle');
Also, there is no need for isAuthenticated; this is redundant, since that state is represented by the finite state (state.value) of your machine.

how can we use data received through axios put request on client side in mern stack?

I have sent category Id to the Nodejs through this code
const catHandler = async (catId) => {
const data = Axios.put('/api/products/categories/filter', {catId: catId},
{
"headers": { "content-type": "application/json", },
}
).then( categoriesProducts => {
console.log(categoriesProducts.data.products)
})
}
and this is my route for this
router.put('/categories/filter', async (req, res) => {
try {
const findCategory = await Category.find({ _id: req.body.catId });
if (findCategory) {
const productsByCategory = await Product.find(
{ category: req.body.catId }
).then(products => {
res.status(200).json({ products });
})
}
} catch (error) {
console.log('categories filter error', error)
}
})
The products of specific category are being shown in the console.log(categoriesProducts.data.products) on the react front end side like below
0: {_id: "5f7c88756746363148792982", name: "Simple Pizza", price: 5.6, image: "1601996916374.jpg", countInStock: 434, …}
1: {_id: "5f7c88976746363148792983", name: "Smoked Pizza", price: 7.8, image: "1601996951114.jpg", countInStock: 88, …}
2: {_id: "5f7c88c56746363148792984", name: "Large Cheezy Pizza", price: 9.4, image: "1601996997474.jpg", countInStock: 434, …}
But I want to display these products on the front end side. I have tried to use axios.get method but with get method how can I can send category Id to backend. So if any one has any idea how to do that Plz guide me.
you can use the query params with get method in node js
you can get query params by req.query in nodeJs
example
passing category id from front end -
api/products/categories/filter?cid=1
getting query param in the backend
const catId = req.query.cid
You can use below code to pass parameter to API get method.
fetch("/api/products/categories/filter?catId" + catId)
.then((res) => res.json())
.then((json) => {
this.setState({
Items: json,
});
});
And also you first create new state named as Items such as below.
this.state = {
Items: []
};
And finally Iterate on Items.
BR

Node js MongoDb specific page view counter

I'm making app with MEAN stack and I want on every get request to increase viewCounter on specific document ( Property ) inside collection.
If i put this code inside get request of requested property
Property.findByIdAndUpdate('id', { $inc: { counter: 1 } }, {new: true})
It will increase loading of data and i want to do that after user gets his data.
So is the best way to do this just to send additional request to the database after initial data is loaded ?
Property {
name: '',
description: '',
...,
viewCounter: 5
}
exports.getProperty = catchAsync(async (req, res, next) => {
query = await Property.findById(req.params.id).lean();
if(!query) {
return next(new AppError('No property found with that ID', 404))
}
res.status(200).json({
status: 'success',
data: {
query
}
})
})
Node events can be used to keep the counter of events.
Official document
Reference for code
eventEmitter.on('db_view', ({ parameters }) => {
eventTracker.track(
'db_view',
parameters
);
})
eventEmitter.on('db_view', async ({ user, company }) => {
Property.findByIdAndUpdate('id', { $inc: { counter: 1 } }, {new: true})
})
Try to send request after making sure your document has loaded.
angular.element($window).bind('load', function() {
//put your code
});

Formik, jest, yup : how to test validation?

i can't find a way to test form yup validation:
it('displays error on submit if name is empty', async () => {
const wrapper = mount(<MyFormik/>)
const getForm = () => wrapper.find('form')
wrapper.find('input[name="name"]').simulate('change', {
persist: () => {},
target: {
name: 'name',
value: ''
}
})
wrapper
.find('MyInnerForm')
.props()
.submitForm()
await wait(0) // await next tick or even 1s...
wrapper.update()
expect(
wrapper
.update()
.find('.error')
.exists()
)
.toBeTruthy() // FALSE!
})
No matter if i wait after submit, update wrapper errors prop is always empty.
And the solution here are not working for me:
https://github.com/jaredpalmer/formik/issues/1146
https://github.com/jaredpalmer/formik/issues/110
Looks like wrapper won't update
Here's the log of formik props after submit:
{ errors: {},
label: '',
name: 'name',
type: 'text',
values: { name: '' },
touched: { name: true },
isValidating: false,
status: undefined,
initialValues: { name: '' },
validateOnChange: true,
validateOnBlur: true } }
...
submitCount: 1,
isValid: false,
You can validate the form values directly on your validation schema.
const yup = require('yup')
const contactSchema = yup.object({
name: yup.string()
.required(),
age: yup.number()
.required()
.positive()
.integer()
})
const errors = await contactSchema.validate({
name: 'Kenneth',
age: -35.5
}).catch(function(err) {
return err
});
console.log("errors", errors);
https://runkit.com/kluplau/5defa8cd122cf6001a3034c7
Without seeing your component I'm not entirely sure what's going wrong. This is likely not to be working:
wrapper
.find('MyInnerForm')
.props()
.submitForm()
If your component MyInnerForm contains a Formik form calling submitForm() there will not cause Formik's validation to run. I would instead do something like this:
wrapper.find("form").simulate("submit");
However if that isn't solving your issue I made a full example that you can have a look at here.

Resources