I am re-creating a basic chat from a previous project (formerly used Bootstrap and Socket) and this go around I am utilizng a React component to render the chat (which consists of an input area for user name, an input area for text content, and a div where the username and message inserted should appear together i.e. "BugsBun01: "Whats up Doc?!"). I am unsure whether or not I should have the empty div where chat content belongs in a separate React component (my end goal is to have the parent component to immediately update the chat area whilst writing the content from the insert fields (username and text content) to the database collection)
I have Mongo database which contains a collection for chats (username/message) but my question is
A) how do I go about using axios to store the inserted username and text in the collection, and
B) once stored how would I allow the parent component (React) to immediately update them to the empty div (chatArea) from the mongo database so that upon page refresh, the users' old chats are still present?
Do I need componentDidMount()?
Do I need any middleware in my server.js file? (bodyparser etc.)
I am fairly new to using React js so bear with me. Also this is a fairly barebones chat that is focusing on functionality.
class Chat extends React.Component {
constructor(props){
super(props);
this.state = {
username: '',
message: '',
messages: []
};
}
render() {
return (
<div id="myChat">
<div id="status"></div>
<input type="text" id="username" class="form-control" placeholder="Enter Username..." value={this.state.username} onChange={ev => this.setState({username: ev.target.value})}>
</input>
<div id="chat">
<br></br>
<div class="card">
<div id="messages" class="card-block">
{this.state.messages.map(message => {
return (
<div>{message.author}: {message.message}</div>
)
})}
</div>
</div>
<br></br>
</div>
<textarea id="textarea" class="form-control" placeholder="Enter message..." value={this.state.message} onChange={ev => this.setState({message: ev.target.value})} ></textarea>
</div>
);
}
}
You have to need ur server for that is not necessary to use axios for that u can manage all thing with ur socket connection.
Server.js that manage ur backend which u want play with the database.
Server.js: implements a start the socket.
const io = require('socket.io')();
const AVATAR = 'https://i1.wp.com/tricksmaze.com/wp-content/uploads/2017/10/Stylish-Girls-Profile-Pictures-11.jpg';
const NAME = '#zoya';
io.on('connection', function (client) {
// console.log('client Id::', client.id)
//chat message
client.on('chat-message', function (messages) {
let { message } = messages;
let messageObj = {
sender: NAME,
avatar: AVATAR,
message
}
client.emit('chat-message', messageObj);
});
//disconnects...
client.on('disconnect', function () {
console.log('disconnect client Id::', client.id)
});
});
const port = 8000;
io.listen(port);
console.log('listening on port : ', port);
on client side.
'use static';
import React, { Component } from 'react';
import openSocket from 'socket.io-client';
const SERVER = `http://localhost:8000/`;
const NAME = '#asif';
const AVATAR = 'https://pbs.twimg.com/profile_images/874276197357596672/kUuht00m_400x400.jpg';
const AVATAR1 = 'https://i1.wp.com/tricksmaze.com/wp-content/uploads/2017/10/Stylish-Girls-Profile-Pictures-11.jpg';
class App extends Component {
constructor(props) {
super(props);
this.state = {
typing: '',
messages: []
}
this.socket = openSocket(SERVER);
this.chatMessage = this.chatMessage.bind(this);
}
componentDidMount() {
this.chatMessage();
}
chatMessage() {
this.socket.on('chat-message', (messageObj) => {
let { messages } = this.state;
messages.push(messageObj);
this.setState({ messages: messages })
})
}
sendMessage = () => {
let { messages, typing } = this.state;
if (typing && typing !== '') {
const message = typing;
this.setState({ typing: '' })
let messageObj = {
sender: NAME,
avatar: AVATAR,
message
}
messages.push(messageObj);
this.setState({ messages: messages })
this.socket.emit('chat-message', messageObj);
} else {
alert(`Message can't empty`);
}
};
renderItem() {
return this.state.messages.map((item,key)=>{
return (
<div >
<image src={ item.avatar } />
<div }>
<span >{item.sender}</span>
<span >{item.message}</span>
</div>
</div>
);
})
}
render() {
return (
<div >
<div >
<h1 >
Chat App
</h1>
</div>
{this.renderItem()}
<div >
<input
Type="text"
ref={ref => { this._messageInput = ref }}
placeholder="Type Message..."
value={this.state.typing}
onChangeText={text => this.setState({ typing: text })}
/>
<button onClick={() => this.sendMessage()}>
<span >Send</span>
</button>
</div>
</div>
);
}
}
export default App;
hope this help full for u.
Related
I am trying to implement eye/eyeslash in on my Register form in React.
This is a function that's is responsible for changing visibility type and eye icon changing.
import React, { useState } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
export const usePasswordToggle = () => {
const [visible, setVisibility] = useState();
const Icon = <FontAwesomeIcon icon={visible ? "eye-slash" : "eye"} />;
const InputType = visible ? "text" : "password";
return [InputType, Icon];
};
I am trying to implement it in component responsible for registering.
import React, { Component, createRef } from "react";
import { usePasswordToggle } from "./usePasswordToggle";
class Register1 extends React.Component {
EmailR = createRef();
UsernameR = createRef();
PasswordR = createRef();
PasswordConfirmR = createRef();
constructor(props) {
super();
this.state = {
message: "",
password: "",
confirmPassword: "",
};
}
handleSubmit = (event) => {
// alert(this.PasswordR.current.value);
// alert(this.PasswordConfirmR.current.value);
if (this.PasswordR.current.value !== this.PasswordConfirmR.current.value) {
alert("The passwords doesn't match");
return false; // The form won't submit
} else {
alert("The passwords do match");
return true; // The form will submit
}
};
onCreateAccount = () => {
let loginInfo = {
Username: this.UsernameR.current.value,
Email: this.EmailR.current.value,
Password: this.PasswordR.current.value,
};
fetch("http://localhost:5000/api/authenticate/register", {
method: "POST",
headers: { "Content-type": "application/json" },
body: JSON.stringify(loginInfo),
})
.then((r) => r.json())
.then((res) => {
if (res) {
this.setState({
message:
"New Account is Created Successfully. Check your email to verify Account.",
});
}
});
};
render() {
return (
<div>
<h2 className="FormDescription">
{" "}
Please enter Account details for registration
</h2>
<div className="Form">
<p>
<label>
Email: <input type="text" ref={this.EmailR} />
</label>
</p>
<p>
<label>
Username: <input type="text" ref={this.UsernameR} />
</label>
</p>
<div>
<label>
Password:{" "}
<input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">
{usePasswordToggle.Icon}
</span>
</div>
<p>
<label>
ReenterPassword:{" "}
<input type="password" ref={this.PasswordConfirmR} />{" "}
</label>
</p>
<button onClick={this.handleSubmit}> Create </button>
<p>{this.state.message}</p>
</div>
</div>
);
}
}
export default Register1;
My password is always visible, and eye icon is even not visible on the form (it should be inside my input field, but it is not).
Focus on this code snippet:
<div>
<label>
Password: <input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">{usePasswordToggle.Icon}</span>
</div>
Any suggestion what is the problem?
Change this
const [visible, setVisibility] = useState();
to this
const [visible, setVisible] = useState(true);
as the official documentation here
First, add a default value to your useState, either true or false depending on which icon you want to render first.
Then, you should add a onClick method to your icon which will toggle the visibility state. You're setting the icon based on visible value, but you never toggle the value.
onClick={() => setVisibility(!visible)}
UPDATE
You also need to execute your Hook inside your main component (because yes, you wrote what React call a Hook), like so :
const [inputType, icon] = usePasswordToggle();
But doing so, you'll get an error from React that say you cannot use a Hook within a class component due to how they work.
Basically you need to change your Register1 component to be a functional component, and not a class anymore. Look here for a quick overview on how to : https://reactjs.org/docs/components-and-props.html
What I want is that, when I click on search button, then a loader/spinner should appear on screen until the data is fetched, when the data is fetched it should disappear.
Container.jsx
import React from 'react';
import './container.css'
import Weather from './weather';
var Loader = require('react-loader');
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
location: "",
weather: [],
loaded:false
};
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
componentDidMount() {
this.setState.loaded=false;
}
continue = (e) => {
this.setState({loaded:true});
const { location } = this.state;
const rawurl = 'http://api.weatherstack.com/current?access_key=d8fefab56305f5a343b0eab4f837fec1&query=' + location;
const url = rawurl;
e.preventDefault();
if (location.length < 1) {
return alert('Enter the details');
}
else {
fetch(url)
.then(response => response.json())
.then(data =>{
this.setState({weather:[data],loaded:false});
})
.catch(err => console.log("error ",err))
}
};
render() {
console.log(this.state.weather);
const weather =
this.state.weather.length> 0 ?
this.state.weather.map(item => (<Weather location={item.location.name} temperature={item.current.temperature} weather={item.current.weather_descriptions[0]} windSpeed={item.current.wind_speed} windDegree={item.current.wind_degree} windDir={item.current.wind_dir} humidity={item.current.humidity} visibility={item.current.visibility} />
))
:<span></span>
return (
<div id="container">
<div class="searchicon">
<input type="search" placeholder="Enter City !!" type="text" name="location" value={this.state.location} onChange={this.handleChange}></input>
<label class="icon">
<button onClick={this.continue} id="btn"><span class="fa fa-search"></span></button>
</label>
</div>
<div>
<Loader loaded={this.state.loaded}>
{weather}
</Loader>
</div>
</div>
);
}
}
export default Container;
What I am using here is react-loader
But right now,its not happening in the way I want, sometime before clicking the serach button it appears and when data is fetched it stops, i want to start it when the api req is made after click on search button and to stop when data is fetched.
first of all you should in the setState after fetching the data to make
this.setState({weather:[data],loaded:true});
second there's another way to do it you can separate the code in the return function like
{ !this.state.loaded ? <Loader loaded={false} options={options} className="spinner" />:{weather}}
as per the Doc in npm you can check it react-loader
I've trying to:
Upload image to firebase storage
create field 'fileUrl' in Firestore under the 'articles' document
reference the doc in Firestore so that each image knows to which article it is assigned.
I managed to do all of that in a way but the issue is that when I submit the form with a title, content and image, it creates a new document in Firestore with just a the image ('fileUrl'), instead of creating a document that includes the rest of the form's data.
I know that this is probably because in my UploadFile.js I'm creating a new document but how do I join the Field created in UploadFile.js with the fields in AddArticle.js?
I'm also getting ''Unhandled Rejection (TypeError): Cannot read property 'state' of undefined' for this line:
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
UploadFile.js
import React from "react";
import firebase from '../Firebase';
function UploadFile() {
const [fileUrl, setFileUrl] = React.useState(null);
const onFileChange = async (e) => {
const file = e.target.files[0]
const storageRef = firebase.storage().ref()
const fileRef = storageRef.child(file.name)
await fileRef.put(file);
setFileUrl(await fileRef.getDownloadURL().then(fileUrl => {
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
.then(() => {
setFileUrl('')
})
} ));
};
return (
<>
<input type="file" onChange={onFileChange} />
<div>
<img width="100" height="100" src={fileUrl} alt=''/>
</div>
</>
);
}
export default UploadFile;
AddArticle.js
import React, { Component } from 'react';
import firebase from '../Firebase';
import UploadFile from '../components/UploadFile';
class AddArticle extends Component {
constructor() {
super();
this.ref = firebase.firestore().collection('articles');
this.state = {
title: '',
content: '',
fileUrl: ''
};
}
onChange = (e) => {
const state = this.state
state[e.target.name] = e.target.value;
this.setState(state);
}
onSubmit = (e) => {
e.preventDefault();
const { title, content, fileUrl } = this.state;
this.ref.add({
title,
content,
fileUrl
}).then((docRef) => {
this.setState({
title: '',
content: '',
fileUrl: '',
documentId: docRef.id
});
this.props.history.push("/")
})
.catch((error) => {
console.error("Error adding document: ", error);
});
}
render() {
const { title, content, fileUrl } = this.state;
return (
<div className="container">
<br></br><br></br><br></br>
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title text-center">
Create a new article
</h3>
</div>
<br></br><br></br>
<div className="panel-body">
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label for="title">Title:</label>
<input type="text" className="form-control" name="title" value={title} onChange={this.onChange} placeholder="Title" />
</div>
<div className="form-group">
<label for="content">Content:</label>
<textArea className="form-control" name="content" onChange={this.onChange} placeholder="Content" cols="80" rows="20">{content}</textArea>
</div>
{/* <input type="file" onChange={this.onFileChange} /> */}
<UploadFile onChange={this.onChange} value={fileUrl}/>
<button type="submit" className="btn btn-success">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
export default AddArticle;
Each time you call firebase.firestore().collection('articles').add(...), you add a new document to the database. Since you call that in both AddArticle and in UploadFile, your image URL and the other fields indeed will end up in separate documents.
The way you're handling the file upload is a bit unusual to me, as I'd normally expect that upload to happen when the other fields are also submitted. Right now, it's not clear to me what the image belong to, as J.A.Hernández also commented.
Either way: you'll need to remove one of the firebase.firestore().collection('articles').add(...) calls and instead pass the document ID into that call. Say that you first add the text fields and then upload the image, the UploadFile can then update the document with:
firebase.firestore().collection('articles').doc(documentId).update({
fileUrl: fileUrl
})
One way to pass the document ID from the to is by keeping it in the state:
this.ref.add({
title,
content,
fileUrl
}).then((docRef) => {
this.setState({
title: '',
content: '',
fileUrl: '',
documentId: docRef.id
});
this.props.history.push("/")
})
And then:
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
I've made a modal for a simple log in page for a website:
import React from 'react';
import { withRouter } from 'react-router-dom';
import '../../assets/stylesheets/session/login_form.css';
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
errors: {}
};
this.handleSubmit = this.handleSubmit.bind(this);
this.renderErrors = this.renderErrors.bind(this);
this.handleDemo = this.handleDemo.bind(this);
}
// After authentication redirect user to home page
componentWillReceiveProps(nextProps) {
if (nextProps.currentUser === true) {
this.props.history.push('/');
this.props.closeModal();
}
// Setting or clearing errors
this.setState({ errors: nextProps.errors });
}
// Hides scrolling when modal is mounted
componentDidMount() {
if (this.props.modal) document.body.style.overflow = 'hidden';
}
// Reactiviates scrolling when modal is unmounted
componentWillUnmount() {
document.body.style.overflow = 'unset';
}
// Render the session errors if there are any
renderErrors() {
return (
<ul>
{Object.keys(this.state.errors).map((error, i) => (
<li key={`error-${i}`}>{this.state.errors[error]}</li>
))}
</ul>
);
}
// Handle field updates
update(field) {
return e =>
this.setState({
[field]: e.currentTarget.value
});
}
// Handle form submission
handleSubmit(e) {
e.preventDefault();
let user = {
email: this.state.email,
password: this.state.password
};
if (this.props.errors) {
this.props.login(user)
.then(() => this.props.openModal('login'));
} else {
this.props.login(user)
.then(() => this.props.closeModal());
}
}
// Handle demo user login
handleDemo(e) {
e.preventDefault();
const user = { email: 'demouser#nookbnb.com', password: 'password' };
this.props.login(user)
.then(this.props.history.push('/'), this.props.closeModal());
}
// Rendering component
render() {
let errors;
if (this.props.errors) {
errors = this.props.errors;
} else {
errors = {};
}
let emailErrors = errors.email ? <div className="email-error">{errors.email}</div> : '';
let passwordErrors = errors.password ? <div className="password-error">{errors.password}</div> : '';
return (
<div className="login-modal-wrapper">
<div className="modal-wrapper" onClick={this.props.closeModal}></div>
<form onSubmit={this.handleSubmit}>
<div className="header-wrapper">
<div className="close-wrapper" onClick={this.props.closeModal}>
<i className="close-button"></i>
</div>
<h1>Log in</h1>
</div>
<div className="main-content-wrapper">
<button onClick={this.handleDemo}>
Demo Log in
</button>
<div className="button-separator-wrapper"><p>or</p></div>
<input
type="text"
value={this.state.email}
onChange={this.update('email')}
placeholder="Email"
/>
<input
type="password"
value={this.state.password}
onChange={this.update("password")}
placeholder="Password"
/>
<div className="session-errors">
{emailErrors}
{passwordErrors}
</div>
<button type="submit">Log in</button>
<div className="no-account-wrapper">
<p>Don't have an account? <span onClick={() => this.props.openModal('signupFirst')}>Sign up</span></p>
</div>
</div>
</form>
</div>
);
}
}
export default withRouter(LoginForm);
And I've successfully displayed the right error messages when the user doesn't enter a required field in the login form (an email and a password), but if I don't manually do a page refresh, the errors still appear on the form even after I close and reopen the modal.
How can I implement this modal in a way where it will automatically clear errors after I close and reopen the modal?
UPDATE
Per the answer below, I've added these two open and closing modal functions to help clear the errors:
// Opens a login modal
openLoginModal() {
this.setState({ errors: {} });
this.props.openModal('login');
}
// Closes a login modal
closeLoginModal() {
this.setState({ errors: {} });
this.props.closeModal();
}
And I've replaced places in the render where I'm using this.props.closeModal() and this.props.openModal() with my functions above. (For now I'm just testing this with closing the modal; since the modal doesn't have any errors when initially opened, I believe I just need to account for closing the modal right now)
<div className="login-modal-wrapper">
<div className="modal-wrapper" onClick={this.closeLoginModal}></div>
<form onSubmit={this.handleSubmit} className={errors.email && errors.email.length !== 0 ? 'form-errors' : 'form-normal'}>
<div className="header-wrapper">
<div className="close-wrapper" onClick={this.closeLoginModal}>
<i className="close-button"></i>
</div>
...
But error messages are still persisting when I open and close the modal.
Perhaps consider having an openLoginModal method that clears any errors and then opens the modal:
openLoginModal() {
this.setState({ errors: {} })
this.props.openModal('login');
}
And then replace any occurrence of this.props.openModal('login') to use this new method (this.openLoginModal()).
Edit: If you need to clear the errors specifically on exiting the modal, you can do a similar thing by creating a custom method around the closeModal prop.
I am playing around with a simple task manager project in react. When I add values to my task list array, the render as expected. However when I add values to my categories array the screen goes blank/white and no messages appear in the error log. Has anyone encountered this or is there an error in my code I am overlooking perhaps? below is the taskbar.js
I have looked through the different console views to try and find any related errors but to no avail. Everything compiles fine.
import React from 'react';
import ReactDOM from 'react-dom';
import TaskList from './taskList.js';
import TaskCategories from './taskCats.js';
class TaskBar extends React.Component
{
constructor(props){
super(props);
this.state = {
tasks:[],
task: '',
categories:["Home","Work","Play"],
cat: ''
};
this.addTask = this.addTask.bind(this);
this.addCat = this.addCat.bind(this);
}
addTask(e){
if(this.state.task !== "")
{
var newTask = {
text: this.state.task,
key: Date.now(),
categories:[]
};
this.setState(() => {
this.state.tasks.push(newTask);
});
this.setState({task: ''});
}
}
addCat(e){
if(this.state.cat !== "")
{
var newCat = {
text: this.state.cat,
key: Date.now(),
tasks:[]
};
this.setState(() => {
this.state.categories.push(newCat);
});
this.setState({cat: ''});
}
}
componentDidUpdate(){
console.log(this.state.tasks);
console.log(this.state.categories);
}
render(){
return (
<div className="inputArea cols-md-6">
<input
value = {this.state.task}
placeholder = "Enter task"
onChange = {(event) =>
this.setState({task:event.target.value})} />
<input type="button" value="Add Task" onClick={this.addTask}
/>
<input
value = {this.state.cat}
placeholder = "Enter category"
onChange = {(event) => this.setState({cat:event.target.value})}
/>
<input type="button" value="Add Category" onClick={this.addCat}
/>
<br /><br />
<div>
<TaskList tasks={this.state.tasks} categories=
{this.state.categories}/>
</div>
</div>
)
}
}
export default TaskBar;
I would expect a new category will be added to the category list.
Better to pass new array and not mutate original and you aren't setting the state property
Try this approach for your arrays
this.setState({tasks: [...this.state.tasks, newTask]});
// and
this.setState({categories: [...this.state.categories, newCat]})