React auto scroll to bottom on a chat container - javascript

I am trying to build a chat page using react. And I have obviously come to a problem where the chat bubbles container doesn't automatically scroll down to bottom on componentDidMount and Update.
I was looking through the previous Q&A but couldn't find any decent solution.
Here is the comoponent.
// renders the text form and all the messages
import React, { Component } from 'react';
import { convo } from '../../data/convo';
import SingleMessage from '../singleMessage/singleMessage';
import StyledForm from './styles';
import moment from 'moment';
class MessageRoom extends Component {
//convo contains the messages
state = {
convo,
message: ''
};
handleChange = e => {
const message = e.target.value;
this.setState({ message });
};
onSubmit = e => {
e.preventDefault();
if (this.state.message) {
const text = {
message: this.state.message,
owner: 0,
date: moment()
};
this.setState({ convo: [...this.state.convo, text], message: '' });
}
};
render() {
return (
<StyledForm>
<div className="messages">
{this.state.convo.map(text => (
<SingleMessage text={text} key={text.date} />
))}
</div>
<div>
<form
onSubmit={e => {
this.onSubmit(e);
}}
>
<input
type="text"
placeholder="Type a message"
value={this.state.message}
onChange={this.handleChange}
/>
<button type="submit"> Send </button>
</form>
</div>
</StyledForm>
);
}
}
export default MessageRoom;
So please help a brother out!

// renders the text form and all the messages
import React, { Component } from 'react';
import { convo } from '../../data/convo';
import SingleMessage from '../singleMessage/singleMessage';
import StyledForm from './styles';
import moment from 'moment';
class MessageRoom extends Component {
constructor() {
super();
this.state = {
convo,
message: ''
};
this.mesRef = React.createRef();
}
componentDidMount() {
this.scrollToBottom();
}
scrollToBottom = () => {
this.mesRef.current.scrollTop = this.mesRef.current.scrollHeight;
};
handleChange = e => {
const message = e.target.value;
this.setState({ message });
};
onSubmit = e => {
e.preventDefault();
if (this.state.message) {
const text = {
message: this.state.message,
owner: 0,
date: moment()
};
this.setState(
{ convo: [...this.state.convo, text], message: '' },
() => {
this.scrollToBottom();
}
);
}
};
render() {
return (
<StyledForm>
<div className="messages" ref={this.mesRef}>
{this.state.convo.map(text => (
<SingleMessage text={text} key={text.date} />
))}
</div>
<div>
<form
onSubmit={e => {
this.onSubmit(e);
}}
>
<input
type="text"
placeholder="Type a message"
value={this.state.message}
onChange={this.handleChange}
/>
<button type="submit"> Send </button>
</form>
</div>
</StyledForm>
);
}
}
export default MessageRoom;
updated the code to the new ref usage.

There is a very simple way to achieve it some css trick
Wrap Html into parent div for message
<div className="message-holder">
<div className="message"> //state all message
...text goes here
</div>
</div>
<style>
.message-holder{
position:absolute;
bottom:0;
//if required overflow to scroll add below css
overflow-y:scroll
max-height: //whatever is required
}
.message{
//css style goes here
}
</style>
Have question ping me

Related

How do I convert this ReactJS class based component to function based component?

I am trying to repeat a inputbox when a user submit a data on a textbox.
The solution I have able to create following through several sources is as:
a simple input box, inputbox.js
import React from 'react';
const InputBox = () => {
return(
<input type="text" placeholder='Add your content line here ... '/>
);
};
export default InputBox;
component that repeats inputbox on Enter or clicking CreateRecursiveInputBox.js
import React from "react";
import styled from "#emotion/styled";
import InputBox from "./inputbox.js";
// creating a bundler component
const Bundle = styled.div`
display: flex;
flex-direction: row;
justify-content: flex-start;
`;
class CreateRecursiveInputBox extends React.Component {
constructor(props) {
super(props);
this.state = {
values: {
0: ''
},
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(index, value) {
this.setState({
values: {
...this.state.values,
[index]: value
}
});
}
handleSubmit(event) {
event.preventDefault();
this.setState({
values: {
...this.state.values,
[Object.keys(this.state.values).length]: '' // add new empty item to list
}
});
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
{ Object.keys(this.state.values).map( (index) => {
const value = this.state.values[index];
return (<div key={ index }>
<Bundle>
<label> "Name" </label>
<InputBox
value={ value }
onChange={ (event) => this.handleChange( index, event.target.value ) }
/>
{/* <input type="submit"/> */}
<input type="submit" value="Submit"/>
</Bundle>
</div>
);
})}
</form>
</div>
);
}
}
export default CreateRecursiveInputBox;
component to render the textbox and repetitive textbox on UI, App.js
import React, { Component } from 'react';
import CreateRecursiveInputBox from './component/CreateRecursiveInputBox.js';
class App extends React.Component {
render(){
return(
<div>
<CreateRecursiveInputBox/>
</div>
)
}
}
export default App;
I am trying to convert the class based component SubmitCreateRecursiveInputBox.js to a function based component. But it is not working out so far.
try this :)
App.js
import React from 'react';
import CreateRecursiveInputBox from './component/CreateRecursiveInputBox.js';
export default function App() {
return (
<div>
<CreateRecursiveInputBox />
</div>
)
}
CreateRecursiveInputBox.js
import React, { useState } from "react";
import InputBox from "./inputbox.js";
// creating a bundler component
const Bundle = styled.div`
display: flex;
flex-direction: row;
justify-content: flex-start;
`;
export default function CreateRecursiveInputBox() {
const [values, setValues] = useState({
values: {
0: ''
},
})
const handleChange = (index, value) => {
setValues({
values: {
...values,
[index]: value
}
});
}
const handleSubmit = (event) => {
event.preventDefault();
setValues({
values: {
...values,
[Object.keys(values).length]: '' // add new empty item to list
}
});
}
return (
<div>
<form onSubmit={handleSubmit}>
{Object.keys(values).map((index) => {
return (<div key={index}>
<Bundle>
<label> "Name" </label>
<InputBox
value={values[index]}
onChange={(event) => handleChange(index, event.target.value)}
/>
{/* <input type="submit"/> */}
<input type="submit" value="Submit" />
</Bundle>
</div>
);
})}
</form>
</div>
);
}

Uncaught TypeError: Object (…) is not a function

I am using Paystack component in my react app,
and the the component requires passing a kind of object containing some required
values below is the mother component for the one holding the paystackButton componenet.
import React from 'react';
import DateRangePicker from 'react-bootstrap-daterangepicker';
import { getRangeOfDates } from 'helpers';
import { BookingModal } from './BookingModal';
import * as moment from 'moment';
import * as actions from 'actions';
import { ToastContainer, toast } from 'react-toastify';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import PScomponent from '../payments/paystack'
class Booking extends React.Component {
constructor() {
super();
this.bookedOutDates = [];
this.dateRef = React.createRef();
this.state = {
errors:[],
proposedBooking: {
startAt: '',
endAt: '',
guests: '',
rooms: '',
totalPrice: '',
days:1
},
modal: {
open: false
}
}
}
............
render() {
const { rental, auth: {isAuth} } = this.props;
const { startAt, endAt, guests, rooms, totalPrice, days } =
this.state.proposedBooking;
const price = (parseInt(totalPrice)+5000) * parseInt(rooms)
const publicKey = "pk_test_........."
const amount = price * 100;
console.log(days)
const componentProps = {
email:this.props.auth.email,
amount,
metadata: {
userName : this.props.auth.username,
phone:this.props.auth.contact,
},
publicKey,
text: "Pay Now",
onSuccess: (e) =>
console.log(e),
onClose: () => alert("Wait! Don't leave :("),
}
//console.log(this.props.auth)
return (
<div className='booking' style={{marginBottom: '10px', padding: '0', border:'none'}}>
<ToastContainer />
<h3 className='booking-price'>NG{rental.dailyRate} <span className='booking-per-night'>per night</span></h3>
<hr></hr>
{ !isAuth &&
<Link className='btn btn-bwm btn-confirm btn-block' to={{pathname: '/login'}}>Login to book place!</Link>
}
{ isAuth &&
<React.Fragment>
<div className='form-group'>
<label htmlFor='dates'>Dates</label>
<DateRangePicker onApply={this.handleApply} isInvalidDate={this.checkInvalidDates} opens='left' containerStyles={{ display: 'block' }}>
<input ref={this.dateRef} id='dates' type='text' className='form-control'></input>
</DateRangePicker>
</div>
<div className='form-group'>
<label htmlFor='guests'>Guests</label>
<input value={guests} onChange={(event) => { this.selectGuests(event) }} type='number' className='form-control' id='guests' aria-describedby='emailHelp' placeholder=''></input>
</div>
<div className='form-group'>
<label htmlFor='guests'>Rooms</label>
<input value={rooms} onChange={(event) => { this.selectRoomNum(event) }} type='number' className='form-control' id='Rooms' aria-describedby='emailHelp' placeholder=''></input>
</div>
<button disabled={!startAt || !endAt || !guests} onClick={() => { this.confirmProposedData() }} className='contact_btn'>PAY FOR RESERVATION</button>
</React.Fragment>
}
{
this.state.modal.open && <PScomponent componentProps={componentProps}/>
}
</div>
)
}
}
function mapStateToProps(state){
return {
auth: state.auth
}
}
export default connect(mapStateToProps)(Booking)
inside PScomponent component .
import React from "react"
import {PaystackButton} from "react-paystack"
import "./P_Scomponent.css"
export default function PScomponent (props){
const { componentProps } = props;
//console.log(componentProps)
return (
<div className="P_Scomponent">
<PaystackButton className="paystack-button" {...componentProps} />
</div>
)
}
//
but yet the browser still give me the following error
i dont know where the error is coming from.
index.es.js:6 Uncaught TypeError: Object(...) is not a function
I finally got it working all I had to do was to downdate to a lower version of the react-paystack the version that worked for me is 2.0.2.

Unable to setState on props in react functional component

I have been unable to setState on props I keep getting
TypeError: props.setState is not a function
I'm trying to implement a search function
const HeroComp = (props) => {
let handleSearchSubmit = (e) => {
props.setState({searchValue: e.target.value});
}
return <div className='heroComp' >
<form action="" >
<input type="text" placeholder='search cartigory' onChange={handleSearchSubmit} />
</form>
</div>
}
export default HeroComp;
When I console.log(props) I get
{searchValue: ""}
searchValue: ""
__proto__: Object
This is the parent component
import images from '../data/images'; //the file from which i'm importing images data
class HomePage extends React.Component{
constructor(){
super();
this.state = {
images,
searchValue: ''
}
}
render(){
const {images , searchValue} = this.state;
const filteredImage = images.filter(image => image.cartigory.toLowerCase().includes(searchValue));
return(
<div >
<HeroComp searchValue={ searchValue } />
<GalleryComp filteredImage={filteredImage} />
</div>
)
}
}
export default HomePage;
I know this should be easy but I just can't see the solution .
How about this?
useEffect(() => {
// set the current state
setSearchValue(props.searchValue)
}, [props]);
Functional component dont have state, but you can use reactHooks:
import React, { useState } from 'react';
const HeroComp = (props) => {
let [searchValue, setSearchValue] = useState();
let handleSearchSubmit = (e) => {
setSearchValue(e.target.value);
}
return <div className='heroComp' >
<form action="" >
<input type="text" placeholder='search cartigory' onChange={handleSearchSubmit} />
</form>
</div>
}
export default HeroComp;

How I can add multiple same fields form in reactJS?

I want to add multiple persons dynamically in my form. Like I have Person 1 username and email then when I click Add Person it should make same fields for person 2 on the same page. When I click the Submit button it should give me the object of all persons.
App.js
import './App.css';
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class App extends Component {
state = {
fields:[]
};
addPerson() {
this.setState({fields:[...this.state.fields, ""]})
};
handleChange(e, index) {
this.state.fields[index] = e.target.value;
this.setState({fields: this.state.fields});
}
handleSubmit(e) {
console.log(this.state,"$$")
}
render() {
return (
<div className="App">
<header className="App-header">
<div>
<h1>The Form</h1>
{
this.state.fields.map((field, index) => {
return(
<div key={index}>
<input onChange={(e)=>this.handleChange(e, index)} value={field}/>
</div>
)
}
)
}
<button onClick={(e) => this.addPerson(e)}>Add Person</button>
<button onClick={(e) => this.handleSubmit(e)}>Submit</button>
</div>
</header>
</div>
)
}
}
I want my state would be like this...
state = {
fields:[
{
id: 1,
name: 'Max',
email: 'max.max#max.in'
}
]
};
Demo of my current page.
This is my solution codesandbox
You need to have two inputs, for email and name, and depending on which input is updated, update the value of person in array.
import React, { Component } from "react";
import "./styles.css";
export default class App extends Component {
state = {
fields: []
};
addPerson() {
const newPerson = {
id: Math.random(),
name: "",
email: ""
};
this.setState({ fields: [...this.state.fields, newPerson] });
}
handleChange(e, index) {
const fieldsCopy = [...this.state.fields];
fieldsCopy.forEach(item => {
if (item.id === index) {
item[e.target.name] = e.target.value;
}
});
this.setState({ fields: fieldsCopy }, () => console.log(this.state.fields));
}
handleSubmit(e) {
console.log(this.state, "$$");
}
render() {
return (
<div className="App">
<header className="App-header">
<div>
<h1>The Form</h1>
{this.state.fields.map(field => {
return (
<div key={field.id}>
<input
onChange={e => this.handleChange(e, field.id)}
name="name"
/>
<input
onChange={e => this.handleChange(e, field.id)}
name="email"
/>
</div>
);
})}
<button onClick={e => this.addPerson(e)}>Add Person</button>
<button onClick={e => this.handleSubmit(e)}>Submit</button>
</div>
</header>
</div>
);
}
}
Edited:
Here is my version of it:
import './App.css';
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class App extends Component {
index = 0;
state = {
fields: []
};
handleChange(e, idx) {
const { name, value } = e.target;
this.setState(state => {
return state.fields[idx][name] = value;
});
}
addPerson = () => {
const person = { id: this.index, name: '', email: '' };
this.index++;
this.setState({ fields: [ ...this.state.fields, person ] })
}
handleSubmit = () => {
console.log(this.state.fields);
}
render() {
const { fields } = this.state;
return (
<div className="App">
<header className="App-header">
<div>
<h1>The Form</h1>
{fields.length
? fields.map((field, idx) => (
<div key={idx}>
<label>Name:</label>
<input type="text" onChange={(e)=>this.handleChange(e, idx)} name="name" value={field.name}/>
<label>Email:</label>
<input type="email" onChange={(e)=>this.handleChange(e, idx)} name="email" value={field.email}/>
</div>
))
: null
}
<button onClick={this.handleSubmit}>Submit</button>
<button onClick={() => this.addPerson()}>Add Person</button>
</div>
</header>
</div>
)
}
}
If you are using the person id as unique identifier outside this component's state, I would suggest using some id generator library like uuid.
I hope this helps!

React.js Form - How To Include All Values On Final Page?

So I have built a Wizard Form using React-Final-Form that I am using in my sign-up page. I am trying to figure out how I can display all user inputs on the final page as a way for the user to double-check/verify their inputs before submitting. Any help would be greatly appreciated!
(P.S. - I tried researching this before posting, but all I was able to find was storing user inputs in Redux and accessing them from there, which I'd like to avoid, if at all possible.)
Here is an example link that shows what I want to do - Please feel free to fork and play around with it if you are trying to figure out a solution! https://codesandbox.io/s/0332k02x0v
Here is the code, shortened to include only the relevant bits:
My Wizard.js page:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Form } from "react-final-form";
class Wizard extends Component {
static propTypes = {
onSubmit: PropTypes.func.isRequired
};
static Page = ({ children }) => children;
constructor(props) {
super(props);
this.state = {
page: 0,
values: props.initialValues || {}
};
}
next = values =>
this.setState(state => ({
page: Math.min(state.page + 1, this.props.children.length - 1),
values
}));
previous = () =>
this.setState(state => ({
page: Math.max(state.page - 1, 0)
}));
validate = values => {
const activePage = React.Children.toArray(this.props.children)[
this.state.page
];
return activePage.props.validate ? activePage.props.validate(values) : {};
};
handleSubmit = values => {
const { children, onSubmit } = this.props;
const { page } = this.state;
const isLastPage = page === React.Children.count(children) - 1;
if (isLastPage) {
return onSubmit(values);
} else {
this.next(values);
}
};
render() {
const { children } = this.props;
const { page, values } = this.state;
const activePage = React.Children.toArray(children)[page];
const isLastPage = page === React.Children.count(children) - 1;
return (
<Form
initialValues={values}
validate={this.validate}
onSubmit={this.handleSubmit}
>
{({ handleSubmit, submitting, values }) => (
<form onSubmit={handleSubmit}>
{activePage}
<div className="buttons">
{page > 0 && (
<button type="button" onClick={this.previous}>
« Previous
</button>
)}
{!isLastPage && <button type="submit">Next »</button>}
{isLastPage && (
<button type="submit" disabled={submitting}>
Submit
</button>
)}
</div>
{/* <pre>{JSON.stringify(values, 0, 2)}</pre> */}
</form>
)}
</Form>
);
}
}
export default Wizard;
My index.js page:
import React, { Component } from "react";
import { Field } from "react-final-form";
import formatString from "format-string-by-pattern";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import Wizard from "./Wizard";
import Styles from "./Styles";
import { addUser } from "../../../actions/authActions";
class ReactFinalForm2 extends Component {
state = {};
render() {
const onSubmit = async values => {
this.props.addUser(values);
// API query here
};
const Error = ({ name }) => (
// Error handing here
);
return (
<Styles>
<div>
<Wizard initialValues={{}} onSubmit={onSubmit}>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page>
{/* *** THIS IS WHERE I WOULD LIKE TO DISPLAY ALL PREVIOUS VALUES (SO THE USER CAN CONFIRM / DOUBLE-CHECK THEIR INPUTS BEFORE SUBMITTING) *** */}
</Wizard.Page>
</Wizard>
</div>
</Styles>
);
}
}
ReactFinalForm2.propTypes = {
addUser: PropTypes.func.isRequired
};
export default connect(
null,
{ addUser }
)(ReactFinalForm2);
I have added a state to the parent component. Changing this state on every submit from the child. I have JSON stringify the state in parent component. As you said no need to use redux, this is the workaround I came with. Still your code has a potential for improvements. Please check this working sandbox:
[ https://codesandbox.io/s/zrvloq4o6x ]
Wizard.js change
handleSubmit = values => {
const { children, onSubmit } = this.props;
const { page } = this.state;
const isLastPage = page === React.Children.count(children) - 1;
if (isLastPage) {
return onSubmit(values);
} else {
this.next(values);
}
// Change added
this.props.onStateChange(values);
};
Index.js
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Field } from "react-final-form";
import Wizard from "./Wizard";
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const onSubmit = async values => {
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const Error = ({ name }) => (
<Field
name={name}
subscribe={{ touched: true, error: true }}
render={({ meta: { touched, error } }) =>
touched && error ? <span>{error}</span> : null
}
/>
);
const required = value => (value ? undefined : "Required");
let data = {};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.onStateChange = this.onStateChange.bind(this);
}
onStateChange = values => {
this.setState({ data: values });
console.log(values);
};
render() {
return (
<Styles>
<h1>🏁 React Final Form Example</h1>
<h2>Wizard Form</h2>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<p>
Notice the mixture of field-level and record-level (or{" "}
<em>page-level</em> in this case) validation.
</p>
<Wizard
initialValues={{}}
onStateChange={this.onStateChange}
onSubmit={onSubmit}
>
<Wizard.Page>
<div>
<label>First Name</label>
<Field
name="firstName"
component="input"
type="text"
placeholder="First Name"
validate={required}
/>
<Error name="firstName" />
</div>
<div>
<label>Last Name</label>
<Field
name="lastName"
component="input"
type="text"
placeholder="Last Name"
validate={required}
/>
<Error name="lastName" />
</div>
</Wizard.Page>
<Wizard.Page
validate={values => {
const errors = {};
if (!values.notes) {
errors.notes = "Required";
}
return errors;
}}
>
<div>
<label>Best Stooge?</label>
<div>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="larry"
/>{" "}
Larry
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="moe"
/>{" "}
Moe
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="curly"
/>{" "}
Curly
</label>
</div>
</div>
<div>
<label>Notes</label>
<Field name="notes" component="textarea" placeholder="Notes" />
<Error name="notes" />
</div>
</Wizard.Page>
<Wizard.Page>
<div>
<p>
<b>Display all previous values here for user verification </b>
<br />
<i>{JSON.stringify(this.state.data, 0, 2)}</i>
</p>
</div>
</Wizard.Page>
</Wizard>
</Styles>
);
}
}
render(<App />, document.getElementById("root"));

Categories

Resources