What is the best practice to "re-render" a Component in React? - node.js

I have an array of items mapped, and when I delete an item, it deletes in the database, and if I manually refresh the page it is gone. However, I want to "re-render" the list of items, once I click the delete button. From what I can find, is I need a state change to "re-render", so I tried setting up a boolean, but It doesn't "re-render". Any suggestions on best practice to achieve this result would be helpful.
class Profile extends Component {
state = {
username: '',
movies: [],
reRender: false <---- by changing this state, I want a re-render of the component
}
getUserMovies = () => {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
axios.post('/getusermovies', {
user: decoded.username
}).then(response => {
this.setState({
username: decoded.username,
// movieName: response.data.moviename
movies: response.data
})
console.log(response.data)
}).catch(error => {
console.log(error + "err in the front end getusermovies function")
})
}
componentDidMount() {
this.getUserMovies();
}
deleteMovie = (itemId) => {
axios.post('/delete', {
movie: itemId
}).catch(err => {
console.log(err + "in the deleteMovie function in front end" + itemId)
})
this.setState(prevState => ({
reRender: !prevState.reRender <------ This is where I am calling a state.
}))
}
render() {
let movies = this.state.movies;
return (
<div className="container">
<h1>{this.state.username}'s Movies: </h1>
<div className="row">
{movies.map(item =>
<div className="profile-movies-box" >
<img src={item.movie} alt='no image' className="movies-box-img" />
<h4>{item.moviename}</h4>
<button className="movie-buttons">
<Link to={{
pathname: `/movie?movieName=${item.moviename}`,
state: { link: item.moviename }
}}>View Movie</Link>
</button>
<button
className="movie-btn"
value={item.moviename}
onClick={() => { this.deleteMovie(item.moviename) }}
>Delete Movie</button>
</div>
)}
</div>
</div>
);
}
}
export default Profile;

You need to update movies array, remove item that has been deleted
deleteMovie = (itemId) => {
axios.post('/delete', {
movie: itemId
})
.then(res => {
this.setState(prevState => ({
movies: prevState.movies.filter(m => m.id !== itemId)
}));
})
.catch(err => {
console.log(err + "in the delteMovie function in front end" + itemId)
})
}
I would recommend using async await
deleteMovie = async (itemId) => {
try {
await axios.post('/delete', {
movie: itemId
});
this.setState(prevState => ({
movies: prevState.movies.filter(m => m.id !== itemId)
}));
} catch (err) {
console.log(err + "in the delteMovie function in front end" + itemId)
}
}

Related

component keeps coming back undefined even tho server prints values

Server
The server prints the groups
I am Trying to retrieve all groups from my Mongodb database but the value keeps returning as undefined.
I am creating a react app but i might be getting confused with Aync and how to use the promise
router.get('/groups', async (req, res) => {
try {
const groups = await Group.find().sort({ date: -1 });
res.json(groups);
console.log(groups);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
Actions
i cant also see the groups being printed here
export const getAllGroups = () => async (dispatch) => {
axios
.get("/api/group/groups")
.then(res =>
dispatch({
type: GET_GROUPS,
payload: res.payload.data
})
)
.catch(err =>
dispatch({
type: GET_GROUPS,
payload: null
})
);
};
Component
this is where i keep getting the undefined when i try to use groups.
class GroupList extends Component {
componentDidMount() => {
const {groups} = await this.props.getAllGroups();
this.props.getAllGroups(groups)
}
// Delete account
onDeleteClick = id => {
const { groups } = this.props;
const groupData = {
id: id,
groups: groups
};
this.props.deleteGroup(groupData);
};
render() {
const { user, groups } = this.props;
let groupItems = groups.map(group => (
<li key={group._id} style={{ marginTop: "1rem" }}>
<button
style={{ marginRight: "1rem" }}
onClick={this.onDeleteClick.bind(this, group._id)}
className="btn btn-small btn-floating waves-effect waves-light hoverable red accent-3"
>
<i className="material-icons">delete</i>
</button>
<b>{group.name}</b>
</li>
));
return (
<div>
<Link to="/" className="btn-flat waves-effect">
<i className="material-icons left">keyboard_backspace</i> Back to
home
</Link>
{ { <ul>{groupItems}</ul> } }
</div>
)
}
}
GroupList.propTypes = {
logoutUser: PropTypes.func.isRequired,
getAllGroups: PropTypes.func.isRequired,
// addAccount: PropTypes.func.isRequired,
deleteGroup: PropTypes.func.isRequired,
groups: PropTypes.array.isRequired,
// plaid: PropTypes.object.isRequired,
user: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
groups: state.groups
});
export default connect(
mapStateToProps,
{ getAllGroups, deleteGroup }
)(GroupList);

Builds a chat app in React, using axios and firestore

Please I would be happy if anyone would help me
I have a problem, I can not use the server-side functions, I call the functions with axios, and execute it in react hooks.
I actually build chat, which is why I use react hook, because I want messages to be updated all the time.
I also use firestore. There I save the messages, and receive them through the server side function.
It's a component of the chat - it's causing me problems, I do not understand why.
The server side functions work great, I tested them in postman, and they worked. The problem is that I can't run them in a function component. I do not know what I'm doing wrong.
The error I get here is in the line chat.users.length> 0?, When I make this comparison I get that chat.users is undefined, but I do not understand why because I initialize it at first, using a server side function , Which gives the necessary information
I'm very confused, and I'm new here on the site, I'm trying to figure out why it has not worked for two whole days
I think I might be confused by syntax, for example using an unnecessary dispatch inside component of the chat
i got this error:
enter image description here
component of the chat
import React, { useEffect, useState } from 'react';
import './style.css';
import { useDispatch, useSelector } from 'react-redux';
import { getRealtimeUsers, updateMessage, getRealtimeConversations } from '../../redux/actions/chatActions';
import { Fragment } from 'react';
const User = (props) => {
const { chat, onClick } = props;
return (
<div onClick={() => onClick(chat)} className="displayName">
<div className="displayPic">
<img src="https://i.pinimg.com/originals/be/ac/96/beac96b8e13d2198fd4bb1d5ef56cdcf.jpg" alt="" />
</div>
<div style={{ display: 'flex', flex: 1, justifyContent: 'space-between', margin: '0 10px' }}>
<span style={{ fontWeight: 500 }}>{chat.firstName} {chat.lastName}</span>
<span className={chat.isOnline ? `onlineStatus` : `onlineStatus off`}></span>
</div>
</div>
);
}
const HomePage = (props) => {
const dispatch = useDispatch();
const user = useSelector(state => state.user.credentials);
const chat = useSelector(state => state.chat);
const [chatStarted, setChatStarted] = useState(false);
const [chatUser, setChatUser] = useState('');
const [message, setMessage] = useState('');
const [userUid, setUserUid] = useState(null);
let unsubscribe;
useEffect(() => {
//unsubscribe = dispatch(getRealtimeUsers(user.handle))
dispatch(getRealtimeUsers());
}, []);
//console.log(user);
//componentWillUnmount
useEffect(() => {
return () => {
//cleanup
//unsubscribe.then(f => f()).catch(error => console.log(error));
unsubscribe.then(f => f()).catch(error => console.log(error));
}
}, []);
//function
const initChat = (chat) => {
setChatStarted(true)
setChatUser(`${chat.firstName} ${chat.lastName}`)
setUserUid(chat.handle);
console.log(chat);
dispatch(getRealtimeConversations({ uid_1: user.handle, uid_2: chat.handle }));
}
const submitMessage = (e) => {
const msgObj = {
user_uid_1: user.handle,
user_uid_2: userUid,
message
}
if (message !== "") {
dispatch(updateMessage(msgObj))
.then(() => {
setMessage('')
});
}
//console.log(msgObj);
}
return (
<Fragment>
<section className="container">
<div className="listOfUsers">
{console.log(chat)}
{
//chat.users != undefined
chat.users.length > 0 ?
chat.users.map(user => {
return (
<User
onClick={initChat}
key={user.handle}
user={user}
/>
);
})
: null
}
</div>
<div className="chatArea">
<div className="chatHeader">
{
chatStarted ? chatUser : ''
}
</div>
<div className="messageSections">
{
chatStarted ?
chat.conversations.map(con =>
<div style={{ textAlign: con.user_uid_1 == user.handle ? 'right' : 'left' }}>
<p className="messageStyle" >{con.message}</p>
</div>)
: null
}
</div>
{
chatStarted ?
<div className="chatControls">
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Write Message"
/>
<button onClick={submitMessage}>Send</button>
</div> : null
}
</div>
</section>
</Fragment>
);
}
export default HomePage;
This is the axios:
app.get('/realtimeUsers', FBAuth, getRealtimeUsers );
app.post('/updateMessage', FBAuth, updateMessage);
app.get('/realtimeConversations', FBAuth, getRealtimeConversations);
And this is the server side functions - They work 100% - I checked them many times and they worked.:
const { db } = require('../util/admin');
exports.getRealtimeUsers = (req, res) => {
db.collection("users")
.onSnapshot((querySnapshot) => {
const users = [];
querySnapshot.forEach(function (doc) {
if (doc.data().handle != req.user.handle) {
users.push(doc.data());
}
});
return res.json(users);
});
}
exports.updateMessage = (req, res) => {
db.collection('conversations')
.add({
...req.body,
isView: false,
createdAt: new Date()
})
.then(() => {
return res.json({ message: "Conversations added successfully" });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
}
exports.getRealtimeConversations = (req, res) => {
console.log(JSON.stringify("testing"));
console.log(JSON.stringify(req.query));
console.log(JSON.parse(req.query.user));
console.log(JSON.parse(req.query.user).uid_1);
console.log(JSON.parse(req.query.user).uid_2);
db.collection('conversations')
.where('user_uid_1', 'in', [JSON.parse(req.query.user).uid_1, JSON.parse(req.query.user).uid_2])
.orderBy('createdAt', 'asc')
.onSnapshot((querySnapshot) => {
const conversations = [];
querySnapshot.forEach(doc => {
console.log(JSON.stringify(doc));
if (
(doc.data().user_uid_1 == JSON.parse(req.query.user).uid_1 && doc.data().user_uid_2 == JSON.parse(req.query.user).uid_2)
||
(doc.data().user_uid_1 == JSON.parse(req.query.user).uid_2 && doc.data().user_uid_2 == JSON.parse(req.query.user).uid_1)
) {
conversations.push(doc.data())
}
});
console.log(conversations);
return res.json(conversations);
})
//return res.json([]);
}
this is the actions that used in the client side, here i call to the axios:
import { userConstants } from "../types";
import axios from 'axios';
export const getRealtimeUsers = () => (dispatch) => {
dispatch({ type: `${userConstants.GET_REALTIME_USERS}_REQUEST` });
axios
.get('/realtimeUsers')
.then((res) => {
console.log(res);
dispatch({
type: `${userConstants.GET_REALTIME_USERS}_SUCCESS`,
payload: res.data
});
})
.catch((err) => console.log(err))
}
export const updateMessage = (msgObj) => (dispatch) => {
axios.post('/updateMessage', msgObj)
.then(() => { })
.catch((err) => console.log(err));
}
export const getRealtimeConversations = (user) => (dispatch) => {
//user = { uid_1: "from visualcode", uid_2: "userUid" };
console.log(JSON.stringify(user));
axios.get('/realtimeConversations',
{
params: {
user: JSON.stringify(user)
//uid_1:JSON.stringify("user.handle"),
//uid_2:JSON.stringify("userUid")
}
}
)
.then((res) => {
dispatch({
type: userConstants.GET_REALTIME_MESSAGES,
payload: res.data
});
})
.catch((err) => console.log(err))
}
I am not able to understand your whole code flow, i.e., how the chat.users will be populated before initChat is called.
But still, for your problem, you should always put a check for undefined values while iterating through an array.
<div className="listOfUsers">
{console.log(chat)}
{
//chat.users != undefined
chat && chat.users && chat.users.length > 0 &&
chat.users.map(user => {
return (
<User
onClick={initChat}
key={user.handle}
user={user}
/>
);
})
}
</div>

mern - updated values are null in data

I'm trying to update the posts. The PUT request in the back end works fine, returning 200 and updates posts when tested on Postman however when I try to update a post in the front end (react), I'm not receiving any errors but the updated post isn't being updated on submit and the updated fields (title and body) are null. The updated values are null when I console.log(data) in the front end which is why they aren't being sent to the back end but they are shown correctly in post.
Why are the updated values null inside data? How can I update the post with the new values instead of getting null?
data:
post:
Updated code: Frontend
const EditPost = ({match}) => {
const [values, setValues] = useState({
title: "",
body: "",
error: ""
});
const [post, setPost] = useState({});
const { user, token } = isAuthenticated();
const {
title,
body,
error,
} = values;
const init = (id) => {
read(id).then(data => {
if (data.error) {
setValues({...values, error: data.error})
} else {
setValues({...values,
title: data.title,
body: data.body,
})
setPost({title: values.title, body: values.body})
}
})
}
useEffect(() => {
const id = match.params.id;
init(id);
}, []);
useEffect(() => {
setPost({...values });
}, [values.title, values.body]);
const handleChange = (name) => (event) => {
setValues({ ...values, [name]: event.target.value });
};
const clickSubmit = (event) => {
event.preventDefault();
setValues({ ...values, error: "" });
editPost(match.params.userId, match.params.id, token, post).then((data) => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({
...values,
title: "",
body: "",
error: false,
});
console.log(post)
console.log(data)
}
});
};
const newPostForm = () => (
<form onSubmit={clickSubmit}>
<div>
<input
onChange={handleChange("title")} type="text"
name="title"
value={title}
/>
</div>
<div className="form-group">
<textarea
onChange={handleChange("body")}
value={body} name="body"
/>
</div>
<button type="submit">Publish</button>
</form>
);
const showError = () => (
<div
style={{ display: error ? "" : "none" }}>
{error}
</div>
);
return (
<div>
{showError()}
{newPostForm()}
</div>
);
};
export default EditPost;
export const editPost = (userId, id, token, post) => {
return fetch(`${API}/${userId}/${id}/edit`, {
method: 'PUT',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify(post)
})
.then(response => {
return response.json();
})
.catch(err => console.log(err));
};
postsByUser.js
<Link className="mypost_btn edit_btn" to={`/${_id}/${post._id}/edit`}>
Edit
</Link>
Backend code
exports.edit = (req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send(`ID is not valid: ${req.params.id}`)
const {title, body} = req.body
const updatedPost = {title, body }
Post.findByIdAndUpdate(req.params.id, {
$set: updatedPost
}, {new:true}, (error, data) => {
if (error) {
return error
} else {
res.send(data)
console.log(data)
}
})
}
Your problem lies here:
editPost(match.params.userId, match.params.id, token, post)
post is not defined.
Since post is not defined, no data is passed. Hence title and body equal to null. What you will need to do is, assuming from what I'm seeing on your code, is to define a state variable called post. I think you intended to do that:
const [post, setPost] = useState({values.title, values.body});
Then ensure that your post is updated whenever your values change using useEffect(),
useEffect(() => {
setPost({...values });
}, [values.title, value.body]);
So by the time you call your editPost() http-put-method, then post has a value. And it should work.
in EditPost.js editPost(match.params.userId, match.params.id, token).then((data) => { here you are missing the 4th arg which is the "post" it self you send to be updated

How can I display data from a server in a material-table?

I use React js on the client side and Node js on the server side and I'm new.
On the client side, I use a button and a table (I use the material-table component)
export default function UserProfile() {
const handleClick = () => {
return axios({
method: "post",
url: "/searchResult",
data: { data }
});
};
return (
<div className="App">
<button onClick={handleClick}> search <button/>
<RemoteData />
</div>
);
}
And RemoteData ...
import MaterialTable from 'material-table';
class RemoteData extends React.Component {
render() {
return (
<MaterialTable
title=""
columns={[
{ title: 'Id', field: 'id' },
]}
data={query =>
new Promise((resolve, reject) => {
let url = '/searchResult1'
fetch(url)
.then(res => res.json())
.then(result => {
resolve({
data: result.data,
})
})
})
}
/>
)
}
}
export default RemoteData;
Clicking the button sends the data from the client to the server and is processed.(To save your time, I write part of the code).
And Server side...
method.post('/searchResult1', searchResult1);
searchResult1: (req, res) => {
let query = "SELECT id FROM `information` WHERE 1=1"
db.query(query, (err, result) => {
if (err) {
res.redirect('/');
}
console.log(result)
})
}
Here we display the 'result' without any problems.
My question is: how can I update and display the 'result' in the material-table with each click of the button?
In material-table documentation you have a section called Remote Data. There is an example with a Refresh button material-ui-table-remote
The code that may interest you:
class RefreshData extends React.Component {
constructor(props) {
super(props);
this.tableRef = React.createRef();
}
render() {
return (
<MaterialTable
title="Refresh Data Preview"
tableRef={this.tableRef}
columns={[
{
title: 'Avatar',
field: 'avatar',
render: rowData => (
<img
style={{ height: 36, borderRadius: '50%' }}
src={rowData.avatar}
/>
),
},
{ title: 'Id', field: 'id' },
{ title: 'First Name', field: 'first_name' },
{ title: 'Last Name', field: 'last_name' },
]}
data={query =>
new Promise((resolve, reject) => {
let url = 'https://reqres.in/api/users?'
url += 'per_page=' + query.pageSize
url += '&page=' + (query.page + 1)
fetch(url)
.then(response => response.json())
.then(result => {
resolve({
data: result.data,
page: result.page - 1,
totalCount: result.total,
})
})
})
}
actions={[
{
icon: 'refresh',
tooltip: 'Refresh Data',
isFreeAction: true,
onClick: () => this.tableRef.current && this.tableRef.current.onQueryChange(), // This line
}
]}
/>
)
}
}

data didn't send to backend properly

I'm using a react and nodejs as backend, backend work properly I guess when I tried in postman but when trying to connect into my frontend it didnt insert the properly
here's the code in react app
class CreateChapter extends React.Component {
constructor(props){
super(props);
this.state={
// editorState: EditorState.createWithContent(convertFromRaw(JSON.parse(content))),
titleChapter: "",
editorState: EditorState.createEmpty(),
id: this.props.location.state.id,
status: '',
errorSaving: '',
saved: false
}
}
// handle editor change
onEditorStateChange = (editorState) => {
this.setState({ editorState })
}
//handle title change
onTitleChange = (event) => {
this.setState({
titleChapter: event.target.value
})
}
//load data chapter saved
loadChapterSaved = (data) => {
this.setState({
titleChapter: data.titleChapter,
editorState: data.editorState,
status: 'saved'
})
}
//load data chapter published
loadChapterPublished = (data) => {
this.setState({
titleChapter: data.titleChapter,
editorState: data.editorState,
status: 'published'
})
}
//save data
onSaveData = () => {
const convertedData = convertToRaw(this.state.editorState.getCurrentContent());
fetch('http://localhost:3001/new-chapter',{
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
id: this.state.id,
editorState: convertedData,
titleChapter: this.state.titleChapter,
status: this.state.status
})
})
.then(response => response.json())
.then( chapter => {
console.log(chapter)
if(chapter === 'empty input'){
this.setState({
errorSaving: 'Please fill Title Chapter and the Content'
})
}else{
this.loadChapterSaved(chapter);
}
})
}
render() {
const { classes, ...rest } = this.props;
const {editorState, id, errorSaving, saved} = this.state;
console.log('status', this.state.status)
return (
<div>
<HeaderHome/>
<div className={classNames(classes.main, classes.mainRaised)}>
<div className={classes.container}>
<div className={classes.storymargin}>
<h2 className={classes.title}>New Chapter</h2>
<Card className={classes.chaptercontainer}>
<CardContent>
<TextField
fullWidth
label=" New Chapter Title"
id="chapter-title"
onChange={this.onTitleChange}
InputProps={{
disableUnderline: true,
classes:{
input: classes.titleinput
}
}}
InputLabelProps={{
shrink: true,
className: classes.titleformlabel
}}
margin="normal"
/>
<Editor
editorState={editorState}
wrapperClassName={classes.homewrapper}
editorClassName={classes.homeeditor}
toolbarClassName={classes.toolbar}
placeholder="Begin typing..."
onEditorStateChange={this.onEditorStateChange}
/>
</CardContent>
{errorSaving ?
<div className={classes.errorSavingChap}>
{errorSaving}
</div>
:
""
}
<CardActions className={classes.displaybutton}>
<Button size="small" clor="primary" className={classes.buttonnextchap}
onClick={this.onSaveData}>
Save
</Button>
<Button size="small" clor="primary" className={classes.buttonnextchap}
onClick={this.onPublishData}>
Publish
</Button>
</CardActions>
</Card>
</div>
</div>
</div>
<div className={classes.footer}>
<Footer />
</div>
</div>
);
} }
export default withStyles(createStoryStyle)(CreateChapter);
the problem is this.state.status didnt send to the backend properly, it always receives initial state which is null. I think it has something to do with how setState is asynchronous, but I'm not quite sure. When I tried it in postman, it does work.
here's my backend
app.post('/new-chapter', (req, res) => {
const { id, titleChapter, editorState, status } = req.body;
if(titleChapter === '' || editorState === ''){
db('story').where('id_user', '=', id)
.increment('chapter', 0)
.returning('chapter')
.then( chapter => {
res.json('empty input')
})
.catch( err => res.json('cannot add chapter'))
}else{
db('story').where('id_user', '=', id)
.increment('chapter', 1)
.returning('chapter')
.then( chapter => {
db.select('entry').from('story').where('id_user', '=',id)
.then( newChap => {
const chapterEntry = newChap[newChap.length - 1].entry;
db('chapter').where('id_user', '=', id)
.returning('*')
.insert({
chapter: chapter[0],
titlechapter: titleChapter,
content: editorState,
id_user: id,
status: status,
entry: chapterEntry,
})
.then( newChapter => {
res.json(newChapter[0])
})
.catch( err => res.json('cannot add new chapter'))
}
)
})
.catch( err => res.json('cannot add new chapter'))
}
})

Resources