JSX not updating to binded State after await - reactjs

I have a login form, which loads a loader when logging in. When login is unsucesful the loader should disappear and the buttons should reappear.
The loader is binded to this.state.loading.
if !this.state.loading it should show buttons (works on first load)
when this.state.loading = true then it should show loader (this works too)
I then run a promise using await async.
then I run this.state.loading = false and it does not update.
login.js:
import React, { Component } from "react";
import { connect } from "react-redux";
import * as actions from "../../actions";
import RoundLoader from "../elements/RoundLoader";
import Logo from "../img/image-center.png";
import "./login.css";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
errorMsg: "",
loading: false
};
this.loginClicked = this.loginClicked.bind(this);
console.log("loggedIn:", this.props.login);
}
async loginClicked() {
try {
var self = this;
self.state.loading = true;
console.log("this1", this);
await this.props.loginUser(
this.refs.email.value,
this.refs.password.value
);
self.state.loading = false; //This has no effect on JSX
if (this.props.login.success) {
this.props.history.push("/");
}
} catch (ex) {
this.state.loading = false;
this.state.errorMsg = "Unable to connect to server";
console.log("error", ex);
}
}
render() {
return (
<div className="login-bg">
<div className="container signForm">
<div className="row back">
<i className="material-icons">arrow_back</i>
Back to Site
</div>
<div className="row login-container z-depth-4">
<div className="col s12 l6 image-bg">
<img className="hbi-logo" src={Logo} alt={"HBI Logo"} />
<h1 className="center-align">
Insurance <br /> Building Company
</h1>
</div>
<div className="col s12 l6 login-form">
<p className="center-align">Login to Trade Portal</p>
<form>
<div className="row">
<div className="input-field">
<input
id="email"
type="email"
className="validate"
ref="email"
/>
<label htmlFor="email">Email</label>
</div>
</div>
<div className="row">
<div className="input-field">
<input
id="password"
type="password"
className="validate"
ref="password"
/>
<label htmlFor="password">Password</label>
</div>
</div>
{!this.state.loading ? ( {/* HERE IS THE IF STATEMENT */}
/* If Not Loading then show Login buttons */
<div>
<div className="row">
<button
className="btn waves-effect waves-light"
type="button"
name="action"
onClick={this.loginClicked}
>
Submit
</button>
<a href="/subcontractor" className="register">
Register here
</a>
</div>
</div>
) : (
/* If Loading then show Loader not Login buttons */
<div className="row">
<div className="col s12 center">
<RoundLoader />
</div>
</div>
)}
<div className="row">
<div style={{ textAlign: "center", color: "red" }}>
{this.props.login.message}
</div>
</div>
</form>
</div>
</div>
</div>
</div>
);
}
}
function mapStateToProps({ login }) {
return { login };
}
export default connect(mapStateToProps, actions)(Login);

try
this.setState({loading: false})
instead of
this.state.loading = false;
https://reactjs.org/docs/react-component.html#setstate
From https://reactjs.org/docs/react-component.html#state :
Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.

Related

How to connect react js voice text to speech into search bar?

I want to add speech recognition for the search bar and I connect it with laravel controller. By typing anything I can search for products. I want to add a microphone and when tell something, search and display results. Accually I want to when tell something , it should display in search bar text. How I do this?
import React,{useState,useEffect} from 'react';
import {Link} from 'react-router-dom';
import axios from 'axios';
import swal from 'sweetalert';
import SpeechRecognition, { useSpeechRecognition } from 'react-speech-recognition';
function VoiceSearchBar() {
const [searchInput,setSearchInput] = useState({
searchtext:'',
});
const [searchResults,setSearchResults] = useState([]);
const handleInput = (e) =>{
e.persist();
setSearchInput({...searchInput,[e.target.name]:e.target.value})
}
const {
transcript,
listening,
resetTranscript,
browserSupportsSpeechRecognition
} = useSpeechRecognition();
if (!browserSupportsSpeechRecognition) {
return <span>Browser doesn't support speech recognition.</span>;
}
const submitSearch = (e) =>{
e.preventDefault();
const data = {
searchtext:searchInput.searchtext,
}
axios.post('api/store-search-text', data).then(res=>{
if(res.data.status === 200){
setSearchResults(res.data.text);
console.log(res.data.text);
}
else if(res.data.status === 404){
swal("Warning",res.data.message,"warning")
}
});
}
var results = "";
if(searchResults){
results = searchResults.map((item)=>{
return(
<div key={item.id} className="row">
<div className="col-sm-3">
<img src={`http://localhost:8000/${item.pimage}`} alt={item.name} width="50px"/>
</div>
<div className="col-sm-9">
<Link to={`/products/category/${item.category.slug}/${item.slug}`}><h6 data-bs-dismiss="modal">{item.name}</h6></Link>
<p>{item.meta_description}</p>
</div>
</div>
)
});
}
return (
<div>
<form onSubmit={submitSearch} className="d-flex">
<input className="form-control me-2" onChange={handleInput} value={searchInput.searchtext} type="search" name="searchtext" placeholder="Search" aria-label="Search" />
<div>
{/* <p>Microphone: {listening ? 'on' : 'off'}</p> */}
{/* <button onClick={SpeechRecognition.startListening}>start</button>
<button onClick={SpeechRecognition.stopListening}><button className="resetBtn" onClick={resetTranscript}></button>Stop</button>
<p>{transcript}</p> */}
</div>
<button className="btn btn-success" type="submit" data-bs-toggle="modal" data-bs-target="#staticBackdrop">Search</button>
</form>
<div class="modal fade" id="staticBackdrop" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Search Results</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{results}
</div>
</div>
</div>
</div>
</div>
);
}
export default VoiceSearchBar;

A component is changing controlled i/p to be uncontrolled. This is caused by value changing from defined to undefined Decide between using controlled/

this is my login.js
import React, { useState } from "react";
// import { BrowserRouter as Router, Route } from "react-router-dom";
import axios from "axios";
import { Redirect } from "react-router-dom";
import Footer from "./Bottom";
// import { FaUser, FaUnlock } from "react-icons/fa";
const Login = () => {
//functional component
const [login, setLogin] = useState({ username: "", password: "" });
// const [isSignUp, setisSignUp] = useState("");
let user;
const handleChange = (e) => {
const name = e.target.name;
const value = e.target.value;
setLogin({ ...Login, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault(); //reload the page
if (login.username && login.password) {
axios
.post("http://localhost:8080/api/registration/login", {
username: login.username,
password: login.password,
})
.then((response) => {
sessionStorage.setItem("user", JSON.stringify(response.data));
user = response.data;
console.log(user + "**********************");
var usertype = response.data.usertype;
console.log(usertype + "////////////");
console.log(user + "*********");
if (usertype === 1) return <Redirect to="/profile" />;
// window.location.href = "http://localhost:3000/profile";
// console.log("Seller");
else if (usertype === 0) return <Redirect to="/buyerprofile" />;
// window.location.href = "http://localhost:3000/buyerprofile";
// console.log("Buyer");
})
.catch((error) => {
console.log(error.response);
});
setLogin({ username: "", password: "" });
}
};
return (
<>
<head>
<link
rel="stylesheet"
href="font-awesome-4.7.0\css\font-awesome.min.css"
/>
</head>
<section className="vh-100">
<div className="container h-100">
<div className="row d-flex justify-content-center align-items-center h-100">
<div className="col-lg-8 col-xl-9">
<div className="card text-black" style={{ borderRadius: "25px" }}>
<div className="card-body p-md-5">
<div className="row justify-content-center">
<div className="col-md-10 col-lg-6 col-xl-7 order-2 order-lg-1">
<p className="text-center h1 fw-bold mb-5 mx-1 mx-md-4 mt-4">
Login
</p>
<form onSubmit={handleChange} className="mx-1 mx-md-4">
<div className="d-flex flex-row align-items-center mb-4">
<i
class="fa fa-user-circle fa-lg"
aria-hidden="true"
></i>
<div className="form-outline flex-fill mb-0">
<input
type="text"
id="username"
name="username"
className="form-control"
placeholder="User Name"
value={login.username}
onChange={handleChange}
/>
{/* {errors.username &&<p className="error">{errors.username}</p>} */}
</div>
</div>
<div className="d-flex flex-row align-items-center mb-4">
<i class="fa fa-unlock fa-lg" aria-hidden="true"></i>{" "}
<div className="form-outline flex-fill mb-0">
<input
type="password"
id="password"
name="password"
className="form-control"
placeholder="Password"
value={login.password}
onChange={handleChange}
/>
{/* {errors.password &&<p className="error">{errors.password}</p>} */}
</div>
</div>
<div className="d-flex justify-content-center mx-4 mb-3 mb-lg-4">
<button
type="submit"
className="btn btn-primary btn-lg"
onClick={handleSubmit}
>
Login
</button>
</div>
</form>
</div>
<div className="col-md-10 col-lg-6 col-xl-5 d-flex align-items-center order-1 order-lg-2">
<img/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<Footer />
</>
);
};
export default Login;
& if I try to login with my credentials it throws this error in console & this warning shows as soon as I start typing in input field
Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. at input
at div
at div
at form
at div
at div
at div
at div
at div
at div
at div
at section
at Login
I am new to react & I was trying my hands with react+sprinboot project, I saw some answers on uncontrolled input to controlled but here it's in-> reverse controlled input to uncontrolled,so what exactly is the difference?
Inside the handleChange function:
setLogin({ ...Login, [name]: value });
i think you meant to write "login" and not "Login", the warning may be caused by that.

why the component is getting unmounted and mounter everytime when the state is changed

Home.JS
class Home extends Component{
state = {
serverPosts:null,
offlinePosts:[],
isLoading:false,
isError:false,
error:null
}
componentDidMount(){
console.log("home did mount")
this.setState({isLoading:true})
axios.get('https://jsonplaceholder.typicode.com/posts')
.then((response)=>{
this.setState({
serverPosts:response.data,
isLoading:false
})
}).catch((err)=>{
this.setState({
isError:true,
error:err
})
})
}
addOfflinePost = (post) => {
const offlineList = [...this.state.offlinePosts,{post}]
this.setState({offlinePosts:offlineList})
}
render(){
console.log("Home component render")
let serverPostList = (this.state.serverPosts)?
this.state.serverPosts.map((item,index)=>{ return <Post postData = {item} key={index}/>}):
(this.state.isError)?<p>No Internet Connection</p>:<p>No Post available</p>
let offlinePostList = (this.state.offlinePosts)?
this.state.offlinePosts.map((item, index)=>{ return <Post postData = {item} key={`id-${index}`}/>}):<button className="btn btn-primary mx-auto" onClick={this.mainContentHandler}>Add Post</button>;
return(
<div className={classes.Home}>
<div className="row py-2">
<div className="col-lg-4">
<div className={"row "+ classes.OfflineList}>
</div>
<div className={"row "+ classes.ServerList}>
{serverPostList}
</div>
</div>
<div className="col-lg-8">
<PostForm click = {this.addOfflinePost}/>
</div>
</div>
</div>
)
}
}
export default Home;
AddForm.JS
class PostForm extends Component{
state = {
title:null,
content:null
}
titleChangeHandler = (event) => {
this.setState({title:event.target.value})
}
contentChangeHandler = (event) => {
this.setState({content:event.target.value})
}
render(){
return (
<div className="card card-primary">
<div className="card-header">
POST
</div>
<div className="card-body">
<form>
<div className="form-group">
<div className="form-group">
<input className="form-control" type="text" placeholder="Title" onChange={this.titleChangeHandler}/>
</div>
<div className="form-group">
<input className="form-control" type="text" placeholder="Content" onChange={this.contentChangeHandler}/>
</div>
<div className="form-group">
<button className="btn btn-primary btn-block" onClick={()=>this.props.click(this.state)}>Submit</button>
</div>
</div>
</form>
</div>
</div>
)}
}
export default PostForm;
Q1. The render function is running multiple times when the component get loaded. When i submit the form then the componentDidMount is running everytime. Please explain me how the flow is working in this component.
Q2. Also if you help me with the best practice of using bootstrap and manual styling together in react

React Ref for sibling component

I have Component named Home which contains LeftNav, BookSearch and HomeCart Component. BookSearch Component has a input field and HomeCart component has a button. When the button in HomeCart component clicked, I need to focus the input field in BookSearch Component.
I researched a bit and I could able to only focus an element from the same Component only. How can I achieve the desired behaviour this using React Ref?
Home.jsx
class Home extends React.Component {
render() {
return (
<React.Fragment>
<div className="row no-gutters app">
<div className="col-sm-12 col-md-12 col-lg-2">
<LeftNav />
</div>
<div className="col-sm-12 col-md-12 col-lg-7 books-panel">
<BookSearch test={this.test}/>
</div>
<div className="col-sm-12 col-md-12 col-lg-3 cart-panel">
<HomeCart onClick={thi} />
</div>
</div>
</React.Fragment>
);
}
}
BookSearch.jsx
class BookSearch extends React.Component {
render() {
return (
<React.Fragment>
<button onClick={this.test}>Focus</button>
<div className="row text-center">
<div className="col-12">
<form onSubmit={this.handleSubmit}>
<h2 className="mt-5">Start searching your favorite books</h2>
<label htmlFor="search-query" className="sr-only">
Search by book name
</label>
<input
name="search-query"
onChange={this.handleChange}
type="search"
className="book-search"
value={this.state.query}
placeholder="Start searching...."
/>
<button type="submit" className="btn btn-primary btn-lg">
Search
</button>
</form>
</div>
</div>
</React.Fragment>
);
}
}
HomeCart.jsx
class HomeCart extends React.Component {
focusSearchBookInput(){
this.props.focusSearchBookInput()
}
render() {
const { cart_items } = this.props;
const cartTable = ();
const noCartItems = (
<React.Fragment>
<p>No items currently available in your cart. </p>
<button onClick={this.focusSearchBookInput} className="btn btn-success mt-5">
<ion-icon name="search" /> Start Searching
</button>
</React.Fragment>
);
return (
<React.Fragment>
<div className="row text-center">
<div className="col-12">
<h2 className="mt-5 mb-5">Books in your cart</h2>
{cart_items.length > 0 ? cartTable : noCartItems}
</div>
</div>
</React.Fragment>
);
}
}
One solution could be...
Create the ref and a click handler that sets focus on the ref in Home.jsx
class Home extends React.Component {
inputRef = React.createRef();
focusHandler = () => {
this.inputRef.current.focus();
}
render() {
return (
<React.Fragment>
<div className="row no-gutters app">
<div className="col-sm-12 col-md-12 col-lg-2">
<LeftNav />
</div>
<div className="col-sm-12 col-md-12 col-lg-7 books-panel">
<BookSearch inputRef={this.inputRef} />
</div>
<div className="col-sm-12 col-md-12 col-lg-3 cart-panel">
<HomeCart focusHandler={this.focusHandler} />
</div>
</div>
</React.Fragment>
);
}
}
Add ref={this.props.inputRef} to the input in BookSearch.jsx
Add onClick={this.props.focusHandler} to the button in HomeCart.jsx

How to change the state of an item affected by onClick in array?

I'm making a page where I need to make multiple selections of buttons (like a filter, which I'll use for the next page).
the information from these buttons is coming from an array and I'm using .map () to mount the button list.
My problem is how do I change the state of only the button that was clicked. The way it is now, when I click, all the buttons are active.
How can I solve this?
Thank you.
import React from 'react';
import { Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import { getLevel, getDiscipline } from '../../functions';
import template from './index.pug';
export default class ConfigAssessment extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
constructor(props){
super(props);
this.state = {
level: getLevel(),
discipline: getDiscipline(),
active: '',
first_click: true,
}
}
changeActive = () => {
if (this.state.first_click === true) {
this.setState({
active: 'active',
first_click: false
});
} else {
this.setState({
active: '',
first_click: true,
});
}
}
render() {
return(
<div className="configuration">
<div className="config-title">
<i className="ti-settings" />
<h2>
<FormattedMessage {...messages.configAssessment} />
</h2>
</div>
<div className="config-items">
<div className="form-group">
<label>
<FormattedMessage {...messages.level} />
</label>
<div className="row">
{this.state.level.map((level, i) => (
<div className="col-xs-1 col-md-4 col-lg-3" key={level.id}>
<button
className={`btn btn-light-gray btn-block ${this.state.active}`}
id={level.id}
onClick={this.changeActive}
>
{level.level}
</button>
</div>
))}
</div>
</div>
<div className="form-group">
<label>
<FormattedMessage {...messages.discipline} />
</label>
<div className="row">
{ this.state.discipline.map((discipline, i) => (
<div className="col-xs-1 col-md-4 col-lg-3" key={i}>
<button
className={`btn btn-light-gray btn-block ${this.state.active}`}
onClick={this.changeActive}
>
{discipline.discipline}
</button>
</div>
))}
</div>
</div>
<div className="form-group">
<label>
<FormattedMessage {...messages.selectQuestion} />
</label>
<div className="row">
<div className="col-xs-1 col-md-4 col-lg-3">
<button
className={`btn btn-light-gray btn-block ${this.state.active}`}
onClick={this.changeActive}
>
<FormattedMessage {...messages.typeAutomatic} />
</button>
</div>
<div className="col-xs-1 col-md-4 col-lg-3">
<button
className={`btn btn-light-gray btn-block ${this.state.active}`}
onClick={this.changeActive}
>
<FormattedMessage {...messages.typeManual} />
</button>
</div>
</div>
</div>
<div className="form-group fg-right">
<Link className="btn btn-warning" to="#">
<FormattedMessage {...messages.createAssessment} />
</Link>
</div>
</div>
</div>
);
}
}
Create a separate component for button
class MyButton extends Component {
constructor(props){
super(props);
this.state = {
person: this.props.person
}
}
buttonActiveHandler = () => {
let oldStatus = this.props.person.status;
this.props.person.status = (!oldStatus ? 'active': '');
this.setState({
person:this.props.person
});
}
render() {
return (
<button className={this.state.person.status} onClick={this.buttonActiveHandler}>{this.state.person.name}</button>
);
}
}
export default MyButton;
Then import button component. use map function to for your code block
<div className={classes.Box}>
<h4>Lorem, ipsum.</h4>
{
this.props.survey.map((person, i) => {
return (
<MyButton key={i} person={person}/>
)
})
}
</div>
The easiest solution to this problem is making the component of the content inside the map and then handling the state of that component there. Hence it will maintain individual states.
It depends what you need.
You can create separate component for button with state.
You can also keep state of each button in react state as an array, and then you can get the state of each button by index.
I'd recommend the first solution, it'd easier to manage such state, but it will be harder to get the state of a specific button from the parent component.

Resources