Update functionality in React - Fetch values and populate onto input boxes - reactjs

I am a newbie to react and I am trying to fetch data and to populate those values onto the input boxes but I couldn't. I don't understand the error.
My code and screenshot is below.
UpdateLevel.js
import React, { Component } from "react";
import Sidebar from '../Sidebar';
import JobService from "../../Services/JobService";
// import '../css/Create.css';
import './../Job/Job.css';
// import '../../Job.css';
import 'react-toastify/dist/ReactToastify.css';
import { Link } from "react-router-dom";
class UpdateLevel extends React.Component{
constructor(props){
super(props)
this.state={
id:0,
level:'',
levelDes:'',
errors:{}
}
this.id=this.id.bind(this);
this.level=this.level.bind(this);
this.levelDes=this.levelDes.bind(this);
this.UpdateInterviewLevel=this.UpdateInterviewLevel.bind(this);
this.cancel=this.cancel.bind(this);
}
componentDidMount(){
console.log(this.props.match);
JobService.GetInterviewLevelById(localStorage.getItem('id')).then((res)=>{
let level=res.data;
this.setState({
id:localStorage.getItem('id')
,level: level.level,
levelDes:level.levelDes
});
});
}
UpdateInterviewLevel=(e)=>{
e.preventDefault();
let errors={};
// var pattern=new RegExp( /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{4,16}$/);
// var patt=new RegExp(/\d$/);
let formvalidstatus=true;
if(this.state.level==""){
formvalidstatus=false;
errors["level"]="Please enter Level !";
}
if((this.state.levelDes)==""){
formvalidstatus=false;
errors["levelDes"]="Please enter Description!";
}
this.setState({
errors:errors
});
if(formvalidstatus==true){
let lev={id:parseInt(this.state.id),
level:this.state.level,
levelDes:this.state.levelDes,
};
console.log('lev=>'+lev.level+ " " + lev.levelDes
);
console.log('lev=>'+JSON.stringify(lev));
JobService.UpdateInterviewLevel(lev.id,lev).then(res=>{
window.location="/viewlevel";
localStorage.setItem('Updatestatus',true);
alert('Interview Level Updated successfully')
});
}
}
id(event){
this.setState({id:event.target.value});
}
level(event){
this.setState({level:event.target.value});
}
levelDes(event){
this.setState({levelDes:event.target.value});
}
cancel(){
alert( window.location.replace('http://localhost:3000/'))
}
render(){
// console.log(this.props.match.params);
return(
<div>
<div class="side">
<Sidebar />
</div>
<form className="addformjob">
<h2><strong>Update Interview Level</strong></h2>
<label>Id</label>
<input id="id" name="id" value={ this.state.id} onChange={this.id}></input><br></br>
{/*
<div className="errorMsgJob">{this.state.errors.id}</div>
*/}
<label>Level</label>
<input id="level" type="text" name="level" value={ this.state.level} onChange={this.level}></input><br></br>
<div className="errorMsgJob">{this.state.errors.level}</div>
<br></br>
<label >Description</label>
<input id="levelDes" type="text" name="levelDes" value={ this.state.levelDes} onChange={this.levelDes}></input><br></br>
<div className="errorMsgJob">{this.state.errors.levelDes}</div>
<br></br>
<br></br>
<button id="jobb" className="btn btn-success" onClick={(e)=>this.UpdateInterviewLevel(e)}>Update</button>
<br></br>
<br></br>
<Link to={'/viewlevel'}>
<button className="btn btn-outline-dark" type="submit" onClick={()=>{window.location='/viewlevel'}}>Back</button>
</Link>
<br></br>
</form>
</div>
)
}
}
export default UpdateLevel
JobService.js
import axios from "axios";
const Job_Baseurl="https://localhost:44348/api/Jobs";
class JobService{
GetAllJobs(){
return axios.get(Job_Baseurl+"/GetAllJobs");
}
Addjob(job){
return axios.post(Job_Baseurl+"/AddJob",job);
}
GetJobById(id){
return axios.get(Job_Baseurl+"/GetJobById?id="+id);
}
UpdateJob(id, job){
return axios.put(Job_Baseurl+"/UpdateJob?id="+id,job);
}
DeleteJob(id)
{
return axios.delete(Job_Baseurl+"/DeleteJob?id="+id);
}
AddInterview(job){
return axios.post(Job_Baseurl+"/AddInterviewLevel",job);
}
GetInterviewLevelById(id){
return axios.get(Job_Baseurl+"/GetInterviewLevelById?id="+id);
}
UpdateInterviewLevel(id,level){
return axios.put(Job_Baseurl+"/UpdateInterviewLevel?id="+id,level);
}
GetAllInterviewLevels(){
return axios.get(Job_Baseurl+"/GetAllInterviewLevels");
}
DeleteInterviewLevel(id)
{
return axios.delete(Job_Baseurl+"/DeleteInterviewLevel?id="+id);
}
}
export default new JobService()
ViewLevel.js
import React, { Component } from "react";
import Sidebar from '../Sidebar';
import JobService from "../../Services/JobService";
import '../css/View.css';
import {toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { Link } from "react-router-dom";
import { confirmAlert } from 'react-confirm-alert'; // Import
import 'react-confirm-alert/src/react-confirm-alert.css'; // Import css
class ViewLevel extends React.Component{
constructor(props){
super(props)
this.state={
levels:[],
currentPage:1,
jobsPerPage:5
}
this.addLevel=this.addLevel.bind(this);
this.editLevel=this.editLevel.bind(this);
this.deleteLevel=this.deleteLevel.bind(this);
this.deleteButton=this.deleteButton.bind(this);
this.searchItems=this.searchItems.bind(this);
}
deleteButton(id)
{
confirmAlert({
title:'Delete Confirmation',
message:'Are you sure you want to delete this ?',
buttons:[
{
label:'Delete',
className:'but1',
onClick:()=> this.deleteLevel(id)
},
{
label:'Cancel',
onClick:()=>window.location='/viewlevel'
}
]
});
}
deleteLevel(id){
JobService.DeleteInterviewLevel(id).then(res=>{
this.setState({levels:this.state.levels.filter(level=>level.id!==id)});
});
window.location='/viewlevel';
localStorage.setItem('Deletestatus',true);
}
editLevel(id){
localStorage.setItem('userid',id);
window.location=`/updatelevel/${id}`;
}
addLevel(){
window.location="/interview";
}
displayToast=()=>{
if(localStorage.getItem('Deletestatus')) {
toast('Deleted Successfully',{
position:toast.POSITION.BOTTOM_CENTER,
type:toast.TYPE.SUCCESS,
toastId:'del'
})
}
if(localStorage.getItem('Resgisterstatus')) {
toast('Added Successfully',{
position:toast.POSITION.BOTTOM_CENTER,
type:toast.TYPE.SUCCESS,
toastId:'add',
})
}
if(localStorage.getItem('Updatestatus')) {
toast('Updated Successfully ',{
position:toast.POSITION.BOTTOM_CENTER,
type:toast.TYPE.SUCCESS,
toastId:'update',
})
}
}
clearData=()=>{
localStorage.removeItem('Resgisterstatus');
localStorage.removeItem('Deletestatus');
localStorage.removeItem('Updatestatus');
}
componentDidMount(){
JobService.GetAllInterviewLevels().then((res)=>{
this.setState({levels:res.data});
this.displayToast();
this.clearData();
});
}
searchItems=(event)=>{
let searchval=event.target.value.toLowerCase();
let res=this.state.levels.filter(level=>{
return(((level.id.toString()).indexOf(searchval)!==-1)||
((level.level.toLowerCase()).indexOf(searchval)!==-1)||
((level.levelDes.toLowerCase()).indexOf(searchval)!==-1))
})
if(res==""||searchval==""){
if(res==""){
toast("Searched value not found",
{
position:toast.POSITION.BOTTOM_CENTER,
type: toast.TYPE.WARNING,
toastId:'search',
autoClose:1000,
onClose:function(){
window.location.reload();
}
})
}
else{
window.location.reload();
}
}
else{
this.setState({
levels:res
})
}
}
render(){
const indexOfLastEmpolyee=this.state.currentPage * this.state.jobsPerPage;
const indexOfFirstEmployee=indexOfLastEmpolyee-this.state.jobsPerPage;
const currentEmployees=this.state.levels.slice(indexOfFirstEmployee,indexOfLastEmpolyee);
const pageNumbers=[];
for(let i=1;i<=Math.ceil(this.state.levels.length/this.state.jobsPerPage);i++){
pageNumbers.push(i);
}
const setPage=(pageNum)=>{
this.setState({currentPage:pageNum})
}
return(
<div className="container">
<div class="side">
<Sidebar />
</div>
<div className="addbtn">
{/* <button className="add" onClick={this.addJob}>Add Job</button> */}
<button className="btn btn-outline-success" type="submit" onClick={()=>{window.location='/interview'}}>Add Interview Level</button>
</div>
{/*
<div className="searchbtn">
<input type="text" placeholder="Search here" onChange={(e)=>this.searchItems(e)}/>
</div>
*/}
.
<br></br>
<br></br>
<br></br>
<h3>Interview Level Details</h3>
<br></br>
<table className="table">
<thead className="dark">
<tr>
<th>Level</th>
<th>Description</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{
currentEmployees.map(items=>(
<tr key={items.id}>
<td>{items.level}</td>
<td>{items.levelDes}</td>
<td><button className="btn btn-primary" onClick={()=>this.editLevel(items.id)}>Edit</button></td>
<td><button className="btn btn-danger" onClick={()=>this.deleteButton(items.id)}>Delete</button></td>
</tr>
))}
</tbody>
</table>
<div className="pagination">
{
pageNumbers.map((pageNum,index)=>(
<span key={index} onClick={()=>{setPage(pageNum)}}>
{pageNum}
</span>
))
}
</div>
</div>
)
}
}
export default ViewLevel
When I am clicking the edit button in ViewLevel.js the values doesn't get populated onto the input boxes and I am getting an error like this in console:
GET https://localhost:44348/api/Jobs/GetInterviewLevelById?id=null 404
Uncaught (in promise) AxiosError {message: 'Request failed with status code 404', name:
'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}
code: "ERR_BAD_REQUEST"
Thanks in advance.

Related

How to render input information and send it to my api's fetch?

I'm having a problem, which is to bring the weather information of a city where the user typed in the input, to my component.
I managed to make it so that when the user typed the city or country, it was already entered as a parameter in my api, but the weather information only appears when I CTRL+S my tsx file.
The same follows in the codes and images below
CityWeatherSearch.tsx
import { MagnifyingGlass } from 'phosphor-react'
import { FormEvent, useRef, useState } from 'react';
import * as Styled from './style'
interface CityPropsP{
city:string,
setCity: typeof useState
}
export function CityWeatherSearch({city,setCity}:CityPropsP){
const inputRef = useRef<HTMLInputElement>(null);
function handleClick(event:FormEvent) {
event.preventDefault();
const inputCity = inputRef?.current?.value;
setCity(inputCity)
}
return(
<>
<Styled.BoxSearchCity>
<div className="headerSearch">
<form onSubmit={handleClick}>
<input type="text" placeholder='Procurar Cidade...' ref={inputRef} />
<button type="submit">
<MagnifyingGlass/>
</button>
</form>
</div>
<div className="bodySearch">
</div>
</Styled.BoxSearchCity>
</>
)
}
MainWeatherLive.tsx
import {Clock} from 'phosphor-react'
import { useState } from 'react'
import { useFetch } from '../../GetData/useFetch'
import * as Styled from './style'
type DataWeather = {
name: string,
condition:{
text:string,
icon:string
},
temp_c:number,
hour:[{
temp_c:number,
time:string,
condition:{
text:string,
icon:string
}
}]
}
interface CityPropsMain{
city:string,
}
export function MainWeatherLive({city}: CityPropsMain){
const {dataCurrent:dataCurrentApi, dataForecast:forecastApi}
= useFetch<DataWeather>(`/v1/forecast.json?key=aff6fe0e7f5d4f3fa0611008221406&q=${city}?days=1&aqi=no&alerts=no`);
console.log(city)
return(
<>
<Styled.HeaderBox>
<h6>Weather Now</h6>
</Styled.HeaderBox>
<Styled.Container>
{city == '' &&
<p>Carregando...</p>
}
<div className="mainInformation">
<div className="temperatura">
<span>{dataCurrentApi?.temp_c}º</span>
</div>
<div>
</div>
<div className="boxCidade">
<div className="cidade">
<span>{city}</span>
</div>
<div className="tempoHoras">
<span>
{new Date().toLocaleTimeString('pt-BR',{hour12:false, hour:'numeric',minute:'numeric'})} - {new Date().toLocaleDateString()}
</span>
</div>
</div>
<div className="iconeTem">
<img src={dataCurrentApi?.condition.icon} alt={dataCurrentApi?.condition.text} />
</div>
</div>
<div className="footerBox">
<div className="headerFooter">
<Clock/>
<span>Horários</span>
</div>
<div className="listaHorarios">
<ul className="boxTT">
{
forecastApi?.hour?.map(weatherA =>{
const hourTemp = weatherA.time.split(" ")[1].replace(":00","");
const hourTempNumber:number = +hourTemp;
const hourNow = new Date().getHours();
return(
<>
{
hourTempNumber == hourNow &&
<li>
<div className="titulo" key={weatherA.temp_c}>
<span>{hourTempNumber}</span>
</div>
<div className="temperatura">
<img src={weatherA.condition.icon} alt={weatherA.condition.text} />
<span>{dataCurrentApi?.temp_c}º</span>
</div>
</li>
}
{
hourTempNumber > hourNow &&
<li>
<div className="titulo" key={weatherA.temp_c}>
<span>{hourTempNumber}</span>
</div>
<div className="temperatura">
<img src={weatherA.condition.icon} alt={weatherA.condition.text} />
<span>{weatherA.temp_c}º</span>
</div>
</li>
}
</>
)
})
}
</ul>
</div>
</div>
</Styled.Container>
</>
)
}
Weather.tsx
import { CityWeatherSearch } from "./WeatherC/CityWeatherSearch";
import { MainWeatherLive } from "./WeatherC/MainWeatherLive";
import { WeatherDetails } from "./WeatherC/WeatherDetails";
import coldImage from '../assets/cold.jpg'
import sunImage from '../assets/sun.jpg'
import rainImage from '../assets/rain.jpg'
import nightVideo from '../assets/night.mp4'
import night from '../assets/night.jpg'
import { useState } from "react";
export const TypesWeather = {
NIGHT:{
video:{
source: nightVideo
},
image:{
source: night
}
},
OVERCAST:{
video:{
source: nightVideo
},
image:{
source: night
}
},
COLD:{
image:{
source: coldImage,
title: 'Frio'
}
},
SUN:{
image:{
source: sunImage,
title: 'Verão'
}
},
RAIN:{
image:{
source: rainImage,
title: 'Chuva'
}
},
};
export type TypesWeatherV2 = keyof typeof TypesWeather;
export function Weather(){
const [city,setCity] = useState('');
return (
<>
<div className="globalSite" style={{background:`linear-gradient(to bottom,rgba(0,0,0,.85) 0,rgba(0,0,0,.85) 100%),url(${TypesWeather.RAIN.image.source})`}}>
</div>
<div className="boxAllWeather">
<div className="backgroundWeather" style={{backgroundImage:`url(${TypesWeather.RAIN.image.source})`}}></div>
<div className="boxAllInff">
<div className="mainWeather">
<MainWeatherLive city={city} />
</div>
<div className="otherInfoWeather">
<CityWeatherSearch city={city} setCity={setCity}/>
<WeatherDetails city={city} setCity={setCity} />
</div>
</div>
</div>
</>
)
}
When I search for a city or state and click search, the name appears normally, but without the updated information
When I save the component responsible for this information, it is updated
I don't know what to do, can anyone give me an idea?

Redirected Component is only appearing for a split-second in ReactJS

This is MasterExcelService.js
import axios from 'axios';
const MASTEREXCEL_API_BASE_URL = "http://localhost:8080/api/masterexcel";
class MasterExcelService{
getMasterExcel(){
return axios.get(MASTEREXCEL_API_BASE_URL);
}
}
export default new MasterExcelService();
This is MasterExcelModule.jsx
import React, { Component } from 'react';
import MasterExcelService from '../services/MasterExcelService';
import HeaderComponent from './HeaderComponent';
class MasterExcelModule extends Component {
constructor(props) {
super(props)
this.state = {
masterexcel: []
}
}
componentDidMount(){
MasterExcelService.getMasterExcel().then((res) => {
this.setState({ masterexcel: res.data});
console.log(res.data);
});
}
render() {
return (
<div>
<HeaderComponent/>
<h2 className='text-center'>Master Excel Module</h2>
<div className = 'row'>
<table className='table table-striped table-bordered'>
<thead>
<tr>
<th>Serial Number</th>
<th>Banner</th>
<th>Chain</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{
this.state.masterexcel.map(
master =>
<tr key = {master.excelID}>
<td>{master.excelID}</td>
<td>{master.banner}</td>
<td>{master.pChain}</td>
</tr>
)
}
</tbody>
</table>
</div>
</div>
);
}
}
export default MasterExcelModule;
This is LoginComponent.jsx
class LoginComponent extends Component {
constructor(props) {
super(props);
this.state = this.initialState;
this.isValid = false
this.validateUser = this.validateUser.bind(this);
}
initialState = {
userID:'', password:'', error:'', loggedIn: false
};
credentialChange = event => {
this.setState({
[event.target.name] : event.target.value
});
};
validateUser = () => {
let user = {
userID: this.state.userID,
password: this.state.password,
};
UserService.loginUser(user).then((res) => {
if(res.data ==='SUCCESS') {
window.sessionStorage.setItem("isUserLogged", true);
this.props.history.push({ pathname: "/master-excel" });
} else if(res.data === 'FAILURE') {
this.resetLoginForm();
window.sessionStorage.setItem("isUserLogged", false);
this.setState({error:"Invalid User ID or Password, please try again!"});
}
})
};
resetLoginForm = () => {
this.setState(() => this.initialState);
};
render() {
const {userID, password, error} = this.state;
return (
<div>
<br></br>
<br></br>
<Row className="justify-content-md-center">
<Col xs={"auto"}>
{error && <Alert variant="danger">{error}</Alert>}
<Card>
<Card.Header className={"border border-light bg-light text-black"}>
<FontAwesomeIcon icon={faSignInAlt}/> Login
</Card.Header>
<br></br>
<form>
<label>
User ID:
<input type="text" name="userID" value={userID} onChange={this.credentialChange} />
</label>
<label>
Password:
<input type="password" name="password" value={password} onChange={this.credentialChange} />
</label>
<Button type="Submit" variant = "outline-success" onClick={this.validateUser} disabled={this.state.userID.length === 0 || this.state.password.length === 0}>Login</Button>
<Button type= "reset" variant = "outline-primary"onClick={this.resetLoginForm}>Reset</Button>
</form>
<br></br>
</Card>
</Col>
</Row>
</div>
);
}
}
export default (LoginComponent);
This is the LandingPage.jsx - Home Page
class LandingPage extends Component {
constructor(props){
super(props)
this.masterExcel = this.masterExcel.bind(this);
this.login = this.login.bind(this);
}
masterExcel(){
this.props.history.push('/master-excel');
}
login(){
this.props.history.push('/login');
}
render() {
return (
<div>
<h1> THIS IS THE LANDING PAGE </h1>
<Button size="sm" type="button" variant="success" onClick={this.login}>
Login
</Button>
{` `}
<Button size="sm" type="button" variant="success" onClick={this.masterExcel}>
Master Excel
</Button>
</div>
);
}
}
export default LandingPage;
This is App.js
function App() {
return (
<div>
<Router>
<div className='container'>
<Switch>
<Route path = "/" exact component={LandingPage}></Route>
<Route path = "/login" exact component={LoginComponent}></Route>
{/* <Route path = "/master-excel" exact component={MasterExcelModule}></Route> */}
<Route path = "/register" exact component={RegisterComponent}></Route>
<Route path="/master-excel" exact render={(props) => (
window.sessionStorage.getItem("isUserLogged") === "true"
? <MasterExcelModule {...props}/>
: <Redirect to='/login' />
)} />
</Switch>
</div>
</Router>
</div>
);
}
export default (App);
So, I am facing an issue with this code. I am trying to redirect the user to a new Component on successful login but the redirected component only stays for a split-second before I am redirected back to my Login Page.
When I open all the components through their links, they open just fine, it is only the redirection/ navigation that poses this problem. Where am I going wrong?
I am using react-router-dom v 5.2.0 and react version 17.0.2
I have solved this. The button type is set to "submit" in the LoginComponent, changing that to "button" instantly resolves the issue!

Modify the state with other component for the input in React

I carry out a project which can modify the price of a product (recovered from a fake API) and then at the click of a button carries out the update by calculating the VAT of 20%. I encounter a problem I would like to have a price state and that in this state it's the value of my input namely {listProduct.price} but it doesn't work.
If you have solutions, I am interested, thank you in advance. (sorry I'm new to React I still have a bit of trouble with all these concepts)
import React, { Component } from 'react'
import '../css/ProductsDetails.css'
import {AiOutlineArrowLeft} from "react-icons/ai";
import {Link} from 'react-router-dom'
export default class ProductsDetails extends Component {
state = {
id: this.props.match.params.id,
price:
}
updatePrice = (e) => {
console.log(e);
this.setState({
price: e.target.value
})
}
render() {
const {location: {state: {listProduct}}} = this.props;
return (
<div className="products__details">
<Link to="/"><AiOutlineArrowLeft className="nav__arrow" /></Link>
<h1 className="details__title">{listProduct.title}</h1>
<div className="details__align--desk">
<div className="details__img">
<img className="product__img" src={listProduct.image} alt="Affichage du produit"/>
</div>
<div className="products__align--desk">
<h2 className="product__title">Description</h2>
<p className="product__description">{listProduct.description}</p>
<h2 className="product__title">Price</h2>
<form className="form__price">
<input className="input__price" type="text" value={listProduct.price} onChange={this.updatePrice} />
<p>Price (including VAT): {Math.round((listProduct.price + listProduct.price * 0.2)*100) /100} €</p>
<br/>
<input className="btn__update" type="submit" value="Update product" />
</form>
</div>
<div className="category__align--desk">
<h2 className="product__title">Category</h2>
<p className="product__category">{listProduct.category}</p>
</div>
</div>
</div>
)
}
}
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {productsData: []};
}
componentDidMount = () => {
axios.get('https://fakestoreapi.com/products?limit=7')
.then(res => {
console.log(res.data)
this.setState ({
productsData: res.data
})
})
}
render() {
const listsProducts = this.state.productsData.map(listProduct => {
return <tbody className="products__body">
<tr>
<td> <Link to={{pathname: "/products-details/" + listProduct.id,state: {listProduct}}}>{listProduct.title}</Link></td>
<td className="products__category">{listProduct.category}</td>
<td>{listProduct.price}</td>
<td>{Math.round((listProduct.price + listProduct.price * 0.2)*100) /100}</td>
</tr>
</tbody>
})
return (
<main className="products">
<h1 className="products__title">Products management</h1>
<table cellSpacing="0">
<thead className="products__head">
<tr>
<th className="table--title">Product name</th>
<th className="table--title">Category</th>
<th className="table--title">Price</th>
<th className="table--title">Price (including VAT)</th>
</tr>
</thead>
{listsProducts}
</table>
</main>
)
}
}
Inside a react component:
1 - You declare the initial state of your component, which is, in this case, the price that the product has before the user writes something. For now, we'll set it to 0:
state = {
id: this.props.match.params.id,
price: this.props.listProduct.price ? this.props.listProduct.price : 0
}
2 - Then, in the render method, we access the price value from this.state
3 - Finally, we modify our input element so that it gets the value of the price.
<input className="input__price" type="text" value={price} onChange={this.updatePrice} />
The rest of the component was working well.
This is the result:
import React, { Component } from 'react'
import '../css/ProductsDetails.css'
import {AiOutlineArrowLeft} from "react-icons/ai";
import {Link} from 'react-router-dom'
export default class ProductsDetails extends Component {
state = {
id: this.props.match.params.id,
price: '0'
}
updatePrice = (e) => {
console.log(e);
this.setState({
price: e.target.value
})
}
render() {
const {price} = this.state
return (
<div className="products__details">
<Link to="/"><AiOutlineArrowLeft className="nav__arrow" /></Link>
<h1 className="details__title">{listProduct.title}</h1>
<div className="details__align--desk">
<div className="details__img">
<img className="product__img" src={listProduct.image} alt="Affichage du produit"/>
</div>
<div className="products__align--desk">
<h2 className="product__title">Description</h2>
<p className="product__description">{listProduct.description}</p>
<h2 className="product__title">Price</h2>
<form className="form__price">
<input className="input__price" type="text" value={price} onChange={this.updatePrice} />
<p>Price (including VAT): {Math.round((listProduct.price + listProduct.price * 0.2)*100) /100} €</p>
<br/>
<input className="btn__update" type="submit" value="Update product" />
</form>
</div>
<div className="category__align--desk">
<h2 className="product__title">Category</h2>
<p className="product__category">{listProduct.category}</p>
</div>
</div>
</div>
)
}
}
Start off with the price at 0 (not in quotes) in state, and then...
const price = this.state.price || (this.props.listProduct ? this.props.listProduct.price : 0)
<input className="input__price" type="text" value={price} onChange{this.updatePrice} />
So if the state value has been updated, that will be used, if not it will check if the price is available in props and use that, and if not it will display zero.

child component holds previous state

Hello i have a list of student
when i click on a student it direct me to student's page
student page is a component(view student details/degrees) that contains a child component(edit-degrees form)
when I go to student1 page first time everything works ok
if I press back and choose student2 then component renders the correct student details but child component (edit-degrees form) shows the degrees of student1 if I go back and go again to student2 twice in a row it will show correct.
Any tip?
Edit: Actually container component also hold previous state
component's while this.props contain the correct current state
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { fetch as fetchStudent } from '../actions/student'
import Title from '../components/Title'
import StudentEditor from './StudentEditor'
//import BatchEditor from './BatchEditor'
class StudentContainer extends PureComponent {
componentWillMount() {
this.props.dispatch(fetchStudent(this.props.match.params.id))
if (this.props){console.log(this.props)}
this.setState(this.props)
}
componentDidMount(){
this.forceUpdate()
}
renderEvaluations(evaluations) {
const evdata = evaluations.map( evaluation => {
let tdstyle= {
background: evaluation.color,
};
return (
<tr>
<td>{evaluation.createdAt}</td>
<td style={tdstyle}>{evaluation.remark}</td>
<td style={tdstyle}>{evaluation.color}</td>
<td>{evaluation.userId}</td>
</tr>);
});
return (
<table border="1">
<tr>
<th>Date</th>
<th>Remark</th>
<th>Color</th>
<th>TeacherID</th>
</tr>
<tbody>
{evdata}
</tbody>
</table>
)
}
render() {
if (this.props.student)
{
var student = this.props.student;
console.log(this.state)
console.log(this.props)
var childprops= this.state.student;
return(
<div className="StudentContainer">
<header>
<Title content={`Student: ${student.name}`} />
</header>
<main>
<div className="studentPhoto">
<img src={student.photo} alt={student.name} />
</div>
<div className="studentDetails">
<div className="title">Name:{student.name}</div>
<div>Evaluations</div>
<div className="evaluations">{this.renderEvaluations(student.evaluations)} </div>
</div>
<StudentEditor student={student} />
</main>
</div>
)} else { return <div>loading</div> }
}
}
const mapStateToProps = ({ student }) => ({ ...student })
export default connect(mapStateToProps)(StudentContainer)
editor
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import 'medium-editor/dist/css/medium-editor.css'
import 'medium-editor/dist/css/themes/default.css'
import updateStudent from '../actions/student/update'
import Title from '../components/Title'
class StudentEditor extends PureComponent {
constructor(props) {
super()
this.state = props.student
this.state.currentUser = "5a3151c868720b1d4cef1b48"
}
updateName(event) {
if (event.keyCode === 13) {
event.preventDefault()
this.refs.name.medium.elements[0].focus()
}
this.setState({
name: this.refs.name.value
})
}
updateRemark(event) {
const index = event.target.id
console.log(index)
if (event.keyCode === 13) {
event.preventDefault()
this.refs.remark.medium.elements[0].focus()
}
const evaluat = this.state.evaluations
evaluat[index].remark = event.target.value
console.log(this.state)
this.setState({evaluations: evaluat })
console.log(this.state)
//const evaluation = this.state.evaluations;
//this.state.evaluations[index].remark = this.refs.remark.value;
this.forceUpdate();
/*
this.setState({
this.state.evaluations[0].remark: this.refs.remark.value
})*/
}
updateColor(event) {
const index = event.target.id
if (event.keyCode === 13) {
event.preventDefault()
this.refs.color.medium.elements[0].focus()
}
//const evaluation = this.state.evaluations;
//this.setState( {evaluations[index]: event.target.value}) //= event.target.value;
const evaluat = this.state.evaluations
evaluat[index].color = event.target.value;
this.setState({evaluations: evaluat })
this.forceUpdate();
}
updatePhoto(event) {
if (event.keyCode === 13) {
event.preventDefault()
this.refs.photo.medium.elements[0].focus()
}
this.setState({
photo: this.refs.photo.value
})
}
addEvaluation() {
const newremark= this.refs.newremark.value
const newcolor= this.refs.newcolor.value
const newuserId= "5a3151c868720b1d4cef1b48"
let newarray= this.state.evaluations.slice()
let neweva= {remark: newremark, color: newcolor, userId:newuserId}
newarray.push(neweva)
const student= {
...this.state
}
student.evaluations=newarray
this.setState(student)
this.props.save(student)
this.forceUpdate()
}
saveStudent() {
console.table(this.state)
const student= {
...this.state
}
console.table(student)
this.props.save(student)
}
renderEvaluationsForm(){
if(this.state.evaluations){
const rendered = this.state.evaluations.map((evaluation,index) => {
if (evaluation.userId === this.state.currentUser){
return (
<div>
<input
type="text"
ref="remark"
className="remark"
placeholder="remark"
onChange={this.updateRemark.bind(this)}
value={this.state.evaluations[index].remark}
id={index} />
<select
ref="color"
className="color"
onChange={this.updateColor.bind(this)}
value={this.state.evaluations[index].color}
id={index}>
<option value="green">green </option>
<option value="orange">orange </option>
<option value="red">red </option>
</select>
</div>
);}});
return rendered;
}
}
render() {
return (
<div className="editor">
<header>
<Title content="Modify Name or Photo" />
</header>
<label>Student's Name:</label>
<input
type="text"
ref="name"
className="name"
placeholder="name"
onChange={this.updateName.bind(this)}
onKeyUp={this.updateName.bind(this)}
value={this.state.name} />
<label>Student's Photo:</label>
<input
type="text"
ref="photo"
className="photo"
placeholder="photo"
onChange={this.updatePhoto.bind(this)}
onKeyUp={this.updatePhoto.bind(this)}
value={this.state.photo}/>
<br /><br />
<div> Modify Evaluations </div><br />
{this.renderEvaluationsForm()}
<div className="actions">
<button className="primary" onClick={this.saveStudent.bind(this)}>Update</button><br />
</div>
<br />
<div> Add new Evaluation </div><br />
<label>Evaluation Remark:</label>
<input
type="text"
ref="newremark"
className="newremark"
placeholder="Add remark"
/>
<label>Evaluation Color:</label>
<select
type="text"
ref="newcolor"
className="newcolor"
>
<option value="green">green</option>
<option value="orange">orange</option>
<option value="red">red</option>
</select>
<div className="actions">
<button className="primary" onClick={this.addEvaluation.bind(this)}>Add Evaluation</button><br />
</div>
</div>
)
}
}
const mapDispatchToProps = { save: updateStudent }
export default connect(null, mapDispatchToProps)(StudentEditor)
Note that you need to implement componentWillReceiveProps(nextProps) method in StudentContainer component and set the state accordingly. componentWillMount() method will be invoked only once before the component is rendered in the DOM.
After that for any props change componentWillReceiveProps() lifecycle hook will be invoked by React

Issue with leave animation using React-Flip-Move

I'm using this module for my twitch API app: https://github.com/joshwcomeau/react-flip-move/
and currently having an issue with the leave animation. The enter animation works perfectly, fine, but unforunately, when I click 'x' on one of the channels, the element (in my case a ) moves up and to the right. How do I make it fade out in its current position?
import React, { Component } from 'react';
import { connect } from 'react-redux';
import FlipMove from 'react-flip-move';
import { selectUser, fetchUser, removeUser } from '../actions/index';
class UsersList extends Component {
constructor(props) {
super(props);
this.state = {
show: 'all',
};
this.fetchInitialUsers(this.props.initialUsers);
}
fetchInitialUsers(users) {
users.map(this.props.fetchUser);
}
renderUser(user) {
const { channelData, streamData } = user;
return (
<tr
key={channelData.display_name}
onClick={() => this.props.selectUser(user)}
className='list-item'>
<td>
<img src={channelData.logo} className='user-logo' />
</td>
<td>
{channelData.display_name}
</td>
<td>
{streamData.stream ?
<span className='online'>Online</span> :
<span className='offline'>Offline</span>}
</td>
<span
className="glyphicon glyphicon-remove"
onClick={() => this.props.removeUser(user)}></span>
</tr>
)
}
showOnline() {
this.setState({
show: 'online'
});
}
showOffline() {
this.setState({
show: 'offline'
});
}
showAll() {
this.setState({
show: 'all'
});
}
render() {
return (
<div className='col-sm-4'>
<div className='text-center'>
<div className='btn-group btn-group-sm' role='group'>
<button
className='btn btn-default'
onClick={this.showAll.bind(this)}>
All
</button>
<button
className='btn btn-default'
onClick={this.showOnline.bind(this)}>
Online
</button>
<button
className='btn btn-default'
onClick={this.showOffline.bind(this)}>
Offline
</button>
</div>
</div>
<div className='container'>
<table className='table table-hover'>
<thead>
<tr>
<th>Logo</th>
<th>Channel</th>
<th>Status</th>
</tr>
</thead>
{/* <tbody> */}
<FlipMove
typeName='tbody' enterAnimation='fade'
leaveAnimation='fade'>
{this.props.users.filter(user => {
const { show } = this.state;
const { streamData } = user;
if (show == 'online') {
return streamData.stream;
}
else if (show == 'offline') {
return !streamData.stream;
}
else {
return user;
}
}).map(this.renderUser.bind(this))}
</FlipMove>
{/* </tbody> */}
</table>
</div>
</div>
)
}
}
function mapStateToProps({ users, initialUsers }) {
return { users, initialUsers };
}
export default connect(mapStateToProps, { selectUser, fetchUser, removeUser })(UsersList);
just add maintainContainerHeight="true" to the flipmove attributes

Resources