How to move document up and down within collection MongoDB - reactjs

I have a simple notes app, where I made it possible for adding, deleting a single note and changing importance. But I can't figure out how to make it moving single note up or down; change the order of notes. My approach is to reorder array of current notes and then somehow post or replace existing collection in my database. But I can't find right tool for this.
I'm using React, Node.js and MongoDB through Mongoose.
P.S: I googled it, and found nothing relevant to this. But I think it's a simple operation and there should be a function for this in MongoDB.
Here is code from my backend index.js file:
app.post('/api/notes', (request, response) => {
const body = request.body;
if (body.content === undefined) {
return response.status(400).json({
error: 'content missing',
});
}
const note = new Note({
content: body.content,
important: body.important || false,
date: new Date(),
});
note.save().then((savedNote) => {
response.json(savedNote);
});
});
app.get('/api/notes/', (request, response) => {
Note.find({}).then((notes) => {
response.json(notes);
});
});
app.get('/api/notes/:id', (request, response, next) => {
Note.findById(request.params.id)
.then((note) => {
if (note) {
response.json(note);
} else {
response.status(404).end();
}
})
.catch((error) => next(error));
});
app.delete('/api/notes/:id', (request, response, next) => {
Note.findByIdAndRemove(request.params.id)
.then((result) => {
response.status(204).end();
})
.catch((error) => next(error));
});
app.put('/api/notes/:id', (request, response, next) => {
const body = request.body;
const note = {
content: body.content,
important: body.important,
};
Note.findByIdAndUpdate(request.params.id, note, { new: true })
.then((updatedNote) => {
response.json(updatedNote);
})
.catch((error) => next(error));
});
Code snippet from frontend:
const App = () => {
const [notes, setNotes] = useState([]);
const [newNote, setNewNote] = useState('');
const [showAll, setShowAll] = useState(true);
const [errorMessage, setErrorMessage] = useState(null);
useEffect(() => {
noteService.getAll().then((initialNotes) => {
setNotes(initialNotes);
});
}, []);
const addNote = (event) => {
event.preventDefault();
const noteObject = {
content: newNote,
date: new Date().toISOString(),
important: Math.random() > 0.5,
id: notes.length + 1,
};
noteService.create(noteObject).then((returnedNote) => {
setNotes(notes.concat(returnedNote));
setNewNote('');
});
};
const handleNoteChange = (event) => {
setNewNote(event.target.value);
};
const toggleImportanceOf = (id) => {
const note = notes.find((n) => n.id === id);
const changedNote = { ...note, important: !note.important };
console.log('Changed');
noteService
.update(id, changedNote)
.then((returnedNote) => {
console.log(returnedNote);
setNotes(notes.map((note) => (note.id !== id ? note : returnedNote)));
})
.catch((error) => {
setErrorMessage(
`Note '${note.content}' was already removed from server`
);
setTimeout(() => {
setErrorMessage(null);
}, 5000);
setNotes(notes.filter((n) => n.id !== id));
});
};
const delNote = (id) => {
window.confirm(`Delete this note?`);
noteService.del(id);
noteService.getAll().then((initialNotes) => {
setNotes(initialNotes);
});
};
const moveUp = (id) => {
const idN = notes.findIndex((n) => n.id === id);
console.log(idN);
//that's where I reorder my current notes(moving up)
let updated = [...notes];
updated.splice(
idN === 0 ? updated.length - 1 : idN - 1,
0,
updated.splice(idN, 1)[0]
);
};
const notesToShow = showAll ? notes : notes.filter((note) => note.important);
And finally code from api (noteService in the code above):
const getAll = () => {
let request = axios.get(baseUrl);
return request.then((res) => res.data);
};
const create = (newObject) => {
const request = axios.post(baseUrl, newObject);
return request.then((res) => res.data);
};
const update = (id, newObject) => {
const request = axios.put(`${baseUrl}/${id}`, newObject);
return request.then((res) => res.data);
};
const del = (id) => {
axios.delete(`${baseUrl}/${id}`);
};

Related

Socket works the for a few seconds then fails

I've managed to establish a connection using socket. It works great for the first few seconds after that it runs super slow takes like almost 2-3 mins to complete a request. And then it produces this error continuously. The app doesn't crash it just runs slowly with the error displaying countless times.
Firefox can’t establish a connection to the server at ws://localhost:5000/socket.io/?EIO=4&transport=websocket&sid=9S6kqHJdHHXQgrobAAHp..
Error on console.
Main.js
function Home(props) {
const [Username , setUsername] = useState("")
const [SearchedData, setSearchedData] = useState()
const [Data, setData] = useState()
const socket = io('http://localhost:5000')
React.useEffect(() => {
// socket.current = io('http://localhost:5000')
socket.emit("content","yada")
socket.on("get-data", data => {
setData(data)
})
})
function NavBar(props){
const handleClick = (e) => {
const {id} = e.target
if(id === "Post-btn"){
if(Content.length > 0){
let data = {
Username: "yada", Content
}
props.socket.emit("store-data", data)
}
}
return(
Tags....
)}
function Content (props) {
const onLike = (e) => {
const { id } = e.target.dataset
const data = {
username: "yada",
id : id
}
// console.log(data)
props.socket.emit("like", data)
}
return(
Tags.....
)
}
server.js
mongoose.connect(process.env.MongoDB,
{ useNewUrlParser: true, useUnifiedTopology: true }).then(() => {
console.log("Database Connected")
}).catch(err => {
console.log(err)
});
const server = app.listen(process.env.Port, () => {
console.log("Connected on " + process.env.Port)
})
const io = socket(server, {
cors:{
origin: "http://localhost:3000",
credential: true,
}
})
let cuid;
io.on("connection", (socket) => {
socket.on("content", username => {
Comments.find({},(err, data) => {
if(!err)
socket.emit("get-data", data)
})
})
socket.on("store-data", data => {
const {Username, Content} = data
const newdata = new Comments({
userName: Username,
content: Content,
createdAt: new Date().toDateString(),
replies: []
})
newdata.save().then(data => {
for(const d in data)
if(d === "_id"){
Users.findOneAndUpdate({username: Username}, {$push: {UserContent: data[d]}}, {new: true}, (err, save) => {
if(err)
console.log(err)
else
console.log(save)
})
}
})
})
socket.on("like", data => {
const {username, id} = data
Users.findOne({username:username}, (err, data) => {
if(!err){
cuid = data['id']
console.log(cuid)
Comments.findByIdAndUpdate(id, {$set: {score: data['_id']}}, {upsert: true}, (err, d) => {
if(!err){
console.log(d)
}
})
}
})
})
})
Looking at the code provided, I noticed there is an useEffect without params. This may be causing a loop until the application crashes.
React.useEffect(() => {
// socket.current = io('http://localhost:5000')
socket.emit("content","yada")
socket.on("get-data", data => {
setData(data)
})
socket.on("Updated", data => {
setData(data)
})
}, []); <- this is missing
This empty array indicates that the content inside the useEffect will only run once.
More about this https://reactjs.org/docs/hooks-intro.html

How can I update updateNotes first then getNotes?

Here it's logging first getNotes then updateNotes
is there a way to first updateNotes then getNotes? Because when I edit and click the left Arrow icon it's not updating changes unless I refresh the page.
Here is the code from NotesPage.js
const NotesPage = () => {
const [notes, setNotes] = useState([]);
useEffect(() => {
getNotes();
console.log('Log from getNotes');
}, [])
const getNotes = async () => {
const response = await fetch("http://localhost:5000/notes");
const data = await response.json()
setNotes(data)
}
and here is the code from NotePage.js
function NotePage({ match, history }) {
const noteId = match.params.id;
const [note, setNote] = useState(null);
useEffect(() => {
getNote();
console.log("Log from getNote");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [noteId]);
const getNote = async () => {
if (noteId === 'new') return
const response = await fetch(`http://localhost:5000/notes/${noteId}`);
const data = await response.json();
setNote(data);
};
const updateNote = async () => {
await fetch(`http://localhost:5000/notes/${noteId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ ...note, "updated": new Date() }),
});
console.log("Log from updateNote");
};
const handeSubmit = () => {
if (noteId !== "new" && !note.body) {
deleteNote();
} else if (noteId !== "new") {
updateNote();
} else if (noteId === 'new' && note !== null) {
createNote()
}
history.push("/");
};
Try getNotes after doing all other actions
Like so:
const handeSubmit = async () => {
if (noteId !== "new" && !note.body) {
await deleteNote();
} else if (noteId !== "new") {
await updateNote();
} else if (noteId === "new" && note !== null) {
await createNote();
}
getNote();
};

Wait for get request response before continuing with a post request in axios

I have tried to use async/await in the createSwap function but it did not work for me. Before creating a request, I want to run the getSlotDetails function to set the slotDets so that it can be used in the post request below. However, the createSwap function does not wait for the getSlotDetails function to complete before sending the post request. How do I fix this?
const [slotDets, setSlotDets] = useState([]);
const getSlotDetails = (moduleCode, slotId, slotType) => {
axios
.get(`https://api.nusmods.com/v2/2020-2021/modules/${moduleCode}.json`)
.then((response) => response.data)
.then((data) => data.semesterData[0])
.then((semesterData) => semesterData.timetable)
.then((timetable) =>
timetable.filter(
(slot) => slot.classNo === slotId && slot.lessonType == slotType
)
)
.then((result) => {
setSlotDets(result);
});
};
const createSwap = (
moduleCode,
slotType,
currentSlot,
desiredSlots,
completed,
reserved
) => {
dispatch(createRequest());
getSlotDetails(moduleCode, currentSlot, slotType);
axios
.post(
"http://localhost:3001/api/v1/swaps",
{
module_code: moduleCode,
slot_type: slotType,
current_slot: currentSlot,
desired_slots: desiredSlots,
completed: completed,
reserved: reserved,
venue: slotDets.venue,
startTime: slotDets.startTime,
endTime: slotDets.endTime,
day: slotDets.day,
},
{
headers,
}
)
.then((response) => {
console.log(response.data);
dispatch(createSuccess());
setTimeout(() => {
dispatch(resetSwap());
}, 2000);
})
.catch((error) => {
console.log(error.response.data);
dispatch(createFail(error.response.data));
setTimeout(() => {
dispatch(resetSwap());
}, 2000);
});
};
const makeTwoRequests = async () => {
try{
const firstResponse = await axios.get(FIRST_URL);
if(firstResponse.status === 200){
const firstResponseData = firstResponse.data;
const secondResponse = await axios.get(SECOND_URL);
if(secondResponse.status === 200){
const secondResponseData = secondResponse.data;
}
}
}
catch(error){
console.log(error)
}
}
maketwoRequests()
firstResponseData and secondResponseData can be state variables instead of const for better access throughout the Component

Reactjs - Firebase : Cancel Old Requests

I'm new to Firebase Realtime Database, and i'm trying to implement a search field that allow users to search for other users and view their profiles.
The Problem Is:
I want to make the search realTime(on each input change).but whenever a new request's sent, the old request is still working in the backend which's causing unexpected behavior,i've wrapped this functionality in a useEffect Hook,old sideEffects has to be cleaned up to make the query results predictable,how can i abort the previous request.
useSearchOwner Custom Hook:
const useSearchOwner = () => {
const [{ SearchValue, SearchResult, Search }, dispatch] = useReducer(
reducer,
{
SearchValue: "",
SearchResult: "",
Search: false,
}
);
const isFirstRender = useRef(true);
const onChangeHandler = (e) =>
dispatch({
type: ACTIONS.UPDATE_SEARCH_VALUE,
payload: { searchValue: e.target.value },
});
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
dispatch({ type: ACTIONS.START_SEARCHING });
const DispatchQueryByResult = async () => {
const ArrayOfOwners = await FirebaseUtilityInstance.SearchOwnerResult(
SearchValue
);
dispatch({
type: ACTIONS.UPDATE_SEARCH_RESULT,
payload: { searchResult: ArrayOfOwners },
});
dispatch({ type: ACTIONS.STOP_SEARCHING });
return () => {
FirebaseUtilityInstance.SearchOwnerCleanup();
};
};
DispatchQueryByResult();
}, [SearchValue]);
useEffect(() => {
console.log(SearchResult);
}, [SearchResult]);
return {
onChangeHandler: onChangeHandler,
Query: SearchValue,
QueryResult: SearchResult,
isSearching: Search,
};
};
Firebase Method To Do Query:
SearchOwnerResult = async (Query) => {
const { firstName, lastName } = getFirstNameAndLastName(Query);
let ArrayOfOwners = [];
await this.Database()
.ref("users")
.orderByChild("UserType")
.equalTo("owner")
.once("value", (snapshot) => {
const OwnersContainer = snapshot.val();
const keys = Object.keys(OwnersContainer);
for (let i = 0; i < keys.length; i++) {
const CurrentOwner = OwnersContainer[keys[i]];
if (
CurrentOwner.FirstName === firstName ||
CurrentOwner.LastName === lastName
) {
ArrayOfOwners.push(OwnersContainer[keys[i]]);
}
}
});
return ArrayOfOwners;
};

How to make Async Await Function in React Native?

I want to create a function that is about uploading photo to Firebase Storage with react-native-fetch-blob. I'm using Redux and you can find action functions below:
My problem is that uploadImage function is not running like asynchronous. Firebase function is running before uploadImage, so application give me an error.
I think i can't make a asynchronous function. How can i fix it ?
uploadImage() function:
const uploadImage = async (imageSource, whereToUpload) => {
let imageURL = '';
const mime = 'image/jpg';
const { Blob } = RNFetchBlob.polyfill;
const { fs } = RNFetchBlob;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
console.log('URI =>', imageSource.uri);
let imgUri = imageSource.uri;
let uploadBlob = null;
const imageRef = firebase.storage().ref(whereToUpload + '/' + imageSource.fileName);
const uploadUri = Platform.OS === 'ios' ? imgUri.replace('file://', '') : imgUri;
await fs.readFile(uploadUri, 'base64')
.then((data) => Blob.build(data, { type: `${mime};BASE64` }))
.then((blob) => {
uploadBlob = blob;
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
uploadBlob.close();
// eslint-disable-next-line no-return-assign
return imageURL = imageRef.getDownloadURL();
})
.catch((error) => {
console.log(error);
});
return imageURL;
};
and the main action is:
export const addProjectGroup = (
myUser,
groupName,
groupDescription,
groupProfilePic,
) => dispatch => {
const groupProfileFinalPic = async () => {
let finalGroupPicture = { landscape: '' };
if (_.isEmpty(groupProfilePic.src)) {
await uploadImage(groupProfilePic, 'groupPictures').then((imageURL) => {
console.log('İŞLEM TAMAM!');
console.log('SELECTED IMAGE URL =>', imageURL);
finalGroupPicture.landscape = imageURL;
});
} else {
finalGroupPicture.landscape = groupProfilePic.src.landscape;
}
return finalGroupPicture;
};
console.log("final group profile pic =>", groupProfileFinalPic());
// Önce grubu yaratalım..
// eslint-disable-next-line prefer-destructuring
const key = firebase
.database()
.ref()
.child('groups')
.push().key;
firebase
.database()
.ref('/groups/' + key)
.set({
admin: {
email: myUser.email,
name: myUser.name,
uid: myUser.uid,
},
groupName,
groupDescription,
groupProfilePic: groupProfileFinalPic(),
projects: '',
})
.then(() => {
console.log('Groups oluşturuldu.');
})
.catch(e => {
Alert.alert('Hata', 'Beklenmedik bir hata meydana geldi.');
console.log(e.message);
});
dispatch({
type: ADD_PROJECT_GROUP,
});
};
You are not awaiting groupProfileFinalPic(). This should be done before creating the action you want to dispatch.
groupProfileFinalPic().then(groupProfilePic => {
return firebase
.database()
.ref("/groups/" + key)
.set({
admin: {
email: myUser.email,
name: myUser.name,
uid: myUser.uid
},
groupName,
groupDescription,
groupProfilePic,
projects: ""
})
.then(() => {
console.log("Groups oluşturuldu.");
})
.catch(e => {
Alert.alert("Hata", "Beklenmedik bir hata meydana geldi.");
console.log(e.message);
});
});
I have no clue what the last dispatch is for, you might want to do that in one of the callbacks. Your code is to verbose for an SO question, but I hope this helps anyways.
You are using both await and then on the same call. To use await, you can arrange it something like
const uploadImage = async (imageSource, whereToUpload) => {
...
try {
let data = await RNFS.fs.readFile(uploadUri, 'base64')
let uploadBlob = await Blob.build(data, { type: `${mime};BASE64` }))
...etc...
return finalResult
catch (e) {
// handle error
}
}

Resources