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.
Related
I have the following Component TBorrowed
import React, { Fragment, useState} from "react";
import {Link} from 'react-router-dom';
const EditItem = ({ item }) => {
const [name, setName] = useState(item.name)
const saveData = async (e) => {
e.preventDefault();
const body = { name}
await fetch(`http://127.0.0.1:5000/item/edit/${item.id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
})
}
return (
<Fragment>
<Link className="link" data-toggle="modal" data-target={`#id${item.id}`} >{item.name}</Link>
<div className="modal" id={`id${item.id}`}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">Edit Item</h4>
</div>
<div className="modal-body">
<label>Name</label>
<input value={name} onChange={e => { setName(e.target.value) }} type="text" />
</div>
<div className="modal-footer">
<button onClick={e => { saveData(e) }} type="button" className="btn btn-outline-success ml-auto" data-dismiss="modal">Save</button>
</div>
</div>
</div>
</div>
</Fragment>
)
}
export default EditItem;
The above is called in another component, Main as shown below
import React, { useState} from 'react';
import TBorrowed from './TBorrowed';
const Main = () => {
const [items, setItems] = useState([]);
...MANY ITEMS SKIPPED...
return (
<table className="layout">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Code</th>
</tr>
</thead>
<tbody>
{
items.map((item, index) => (
<tr key={item.id}>
<td>{index + 1}</td>
<td>{item.name}</td>
<td>{<TBorrowed item={item} />}</td>
</tr>
))
}
</tbody>
</table>
)
}
export default Main;
The above works well where I am able to see the item code in the Main component's <td></td> when rendered, which when I click, I am able to edit the particular item in a modal.
My issue is I no longer want to edit an item in a modal but I want it rendered on it's own page for editing.
When I try it without a data-toggle = "modal" in the TBorrowed component, I get all the contents of the TBorrowed component displaying in the Main component where the modal is called i.e <td>{<TBorrowed item={item} />}</td>. All the data in TBorrowed is shown in that <td></td> instead of just the item.code as it was showing while using the modal
My code has some parts missing so it can fit here.
Please assist, and if there's more information required I'll provide it.
In my project I'm using redux toolkit and react router v6. I have invoices list with 'View' button and when it's clicked it should open page with description about invoice. Also i have add invoice feature. When invoice added and I click on 'View' button page crashes and the error in console say:
Uncaught TypeError: Cannot read properties of undefined (reading 'invoice_num')
And the same happens if i click on existing item and reload the page. It says the error occured in InvoiceItem.js page.
Now the code. InvoiceItem.js
import React from "react";
import { useParams } from "react-router-dom";
import InvoiceItemDescription from "../Invoice/InvoiceItemDescription";
import { INVOICES_LIST } from "./InvoicesList";
const InvoiceItem = () => {
const params = useParams();
const invoice = INVOICES_LIST.find(
(invoice) => invoice.id === params.invoiceId
);
return (
<InvoiceItemDescription
invoiceNumber={invoice.invoice_num}
status={invoice.status}
order_date={invoice.order_date}
bill_from={invoice.bill_from}
bill_from_address={invoice.bill_from_address}
bill_from_email={invoice.bill_from_email}
bill_from_fax={invoice.bill_from_fax}
bill_from_phone={invoice.bill_from_phone}
bill_to={invoice.bill_to}
bill_to_address={invoice.bill_to_address}
bill_to_email={invoice.bill_to_email}
bill_to_fax={invoice.bill_to_fax}
bill_to_phone={invoice.bill_to_phone}
item_name={invoice.ITEMS.item_name}
unit_costs={invoice.ITEMS.unit_costs}
unit={invoice.ITEMS.unit}
price={invoice.ITEMS.price}
/>
);
};
export default InvoiceItem;
InvoiceItemDescription.js file
import React from "react";
import Wrapper from "../../UI/Wrapper";
import Footer from "../../UI/Footer";
import classes from "./InvoiceItemDescription.module.css";
import { Link } from "react-router-dom";
const InvoiceItemDescription = (props) => {
let counter = 1;
return (
<Wrapper isShrinked={props.isShrinked}>
<div className={classes.wrapper}>
<div className={classes["content-wrapper"]}>
<div className={classes["main-wrapper"]}>
<div className={classes["upper-buttons"]}>
<div className={classes["upper-buttons-wrapper"]}>
<Link to="/invoices">
<button type="button" className={classes["go-to-invoices"]}>
Go To Invoices
</button>
</Link>
<Link to="/invoices/edit-invoice">
<button type="button" className={classes["edit-invoice"]}>
Edit Invoice
</button>
</Link>
</div>
</div>
<div className={classes.content}>
<div className={classes["invoice-info"]}>
<div className={classes.info}>
<h3>Invoice Info</h3>
<span>{props.invoiceNumber}</span>
</div>
<div className={classes.order}>
<p>
<span className={classes["order-status"]}>
Order Status:
</span>
<span className={classes.status}>{props.status}</span>
</p>
<p>
<span className={classes["order-date"]}>Order Date:</span>
<span className={classes.date}>{props.order_date}</span>
</p>
</div>
</div>
<div className={classes.bills}>
<div className={classes["bill-from"]}>
<h3>Bill From</h3>
<div>
<p className={classes["bill-from-info"]}>
<span className={classes.name}>{props.bill_from}</span>
<span className={classes.email}>
{props.bill_from_email}
<br></br>
<br></br> {props.bill_from_address}
<br></br>
<br></br>
<br></br> {props.bill_from_phone}
</span>
</p>
</div>
</div>
<div className={classes["bill-to"]}>
<h3>Bill To</h3>
<p className={classes["bill-to-info"]}>
<span className={classes.name}>{props.bill_to}</span>
<span className={classes.email}>
{props.bill_to_email} <br></br>
<br></br> {props.bill_to_address} <br></br>
<br></br>
<br></br>
{props.bill_to_fax} <br></br> {props.bill_to_phone}
</span>
</p>
</div>
</div>
<div className={classes.table}>
<table>
<colgroup>
<col className={classes.col1}></col>
<col className={classes.col2}></col>
<col className={classes.col3}></col>
<col className={classes.col4}></col>
<col className={classes.col5}></col>
</colgroup>
<thead>
<tr>
<td>#</td>
<td>Item Name</td>
<td>Unit Costs</td>
<td>Unit</td>
<td>Price</td>
</tr>
</thead>
<tbody>
<tr>
<td>{counter++}</td>
<td>{props.item_name}</td>
<td>{props.unit_costs}</td>
<td>{props.unit}</td>
<td>{props.price}</td>
</tr>
</tbody>
</table>
</div>
<div className={classes.total}>
<p>
Sub-total:
<span>$13300</span>
</p>
<p>
Vat:
<span>$13300</span>
</p>
<h3>
Grand Total:
<span>$14630</span>
</h3>
</div>
</div>
<div className={classes["lower-btn"]}>
<button type="button">Send Invoice</button>
</div>
</div>
</div>
</div>
<Footer />
</Wrapper>
);
};
export default InvoiceItemDescription;
And Invoice.js file
import React from "react";
import classes from "./Invoice.module.css";
import { useDispatch } from "react-redux";
import { Link } from "react-router-dom";
import { invoiceActions } from "../../store/invoice-slice";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faTrash } from "#fortawesome/free-solid-svg-icons";
const Invoice = (props) => {
const { id, invoice_num, bill_from, bill_to, status } = props.invoiceItem;
const dispatch = useDispatch();
const removeInvoiceItem = () => {
dispatch(invoiceActions.removeInvoice(id));
};
return (
<tr className={classes.height}>
<td>
<span className={classes.checkbox}>
<input type="checkbox"></input>
</span>
</td>
<td>
<span>{invoice_num}</span>
</td>
<td>
<span>{bill_from}</span>
</td>
<td>
<span>{bill_to}</span>
</td>
<td>
<span>14300</span>
{/* This should be a dynamic value later */}
</td>
<td>
<span
className={`${
status === "Pending" ? classes["status-pending"] : ""
} ${status === "Delivered" ? classes["status-delivered"] : ""} ${
status === "Shipped" ? classes["status-shipped"] : ""
}`}
>
{status}
</span>
</td>
<td>
<div className={classes.buttons}>
<Link to={`/invoices/invoice-description/${id}`}>
<button className={classes["view-btn"]}>View</button>
</Link>
<button className={classes["delete-btn"]} onClick={removeInvoiceItem}>
<FontAwesomeIcon icon={faTrash} />
</button>
</div>
</td>
</tr>
);
};
export default Invoice;
I have no idea what can cause the crash of the page. Can someone help me with this, please?
P.S. here is my github repo(it's my PET project) - https://github.com/stepan-slyvka/test-project
The issue is that in the INVOICES_LIST test invoice data exported from src/components/Pages/Invoice/InvoicesList.js you are creating randomly generated id properties. When the page reloads, the entire app reloads, INVOICES_LIST is exported with all new id properties. The id that is read from the URL path is no longer valid and InvoiceItem can't render an invoice object that is undefined.
export const INVOICES_LIST = [
{
id: Math.random().toString(), // <-- random each time app loads
...
},
{
id: Math.random().toString(), // <-- random each time app loads
...
},
{
id: Math.random().toString(), // <-- random each time app loads
...
},
];
You really want GUIDs to be stable, and more determinant and guaranteed for uniqueness, so don't use Math.random to create them, use something more like uuid if you need to generate unique ids.
To resolve your specific issue the fix is to just hardcode a unique id value. Even just complete gibberish, so long as it uniquely identifies an object, is sufficient (for testing).
Example:
export const INVOICES_LIST = [
{
id: '09u34otiuhnrfgp9ioj45',
...
},
{
id: '234098ujh43gikoljaerpgiojaerg',
...
},
{
id: '0934tpinr-9ujw3ensdsf',
...
},
];
In the InvoiceItem component that is searching the INVOICES_LIST array keep in mind that Array.prototype.find potentially returns undefined when no match is found. The UI should handle this. Conditionally render the InvoiceItemDescription only if there is a found invoice.
Example:
const InvoiceItem = () => {
const { invoiceId } = useParams();
const invoice = INVOICES_LIST.find((invoice) => invoice.id === invoiceId);
return invoice ? (
<InvoiceItemDescription
invoice_num={invoice.invoice_num}
status={invoice.status}
order_date={invoice.order_date}
bill_from={invoice.bill_from}
bill_from_address={invoice.bill_from_address}
bill_from_email={invoice.bill_from_email}
bill_from_fax={invoice.bill_from_fax}
bill_from_phone={invoice.bill_from_phone}
bill_to={invoice.bill_to}
bill_to_address={invoice.bill_to_address}
bill_to_email={invoice.bill_to_email}
bill_to_fax={invoice.bill_to_fax}
bill_to_phone={invoice.bill_to_phone}
item_name={invoice.ITEMS.item_name}
unit_costs={invoice.ITEMS.unit_costs}
unit={invoice.ITEMS.unit}
price={invoice.ITEMS.price}
/>
) : (
<div>No Invoices Found.</div>
);
};
So, issue is fixed and to conclude, maybe someone is facing the same problem, I'll write a few words.
Firstly, when you have static data like I had(3 invoices which renders each time page loads) - hardcode your id's.
Secondly, for newly created items use stable id's(example - uuid) and store it somewhere(for example in local storage).
Thirdly, if page crashes or something renders wrong - check your state! In my case issue was in InvoiceItem.js file where i was trying to render INVOICES_LIST instead of selecting my state with const invoices = useSelector((state) => state.invoice.invoices)
Hope, this helps someone like me - novice in redux :)
P.S. final code is here
What I am Trying to do is to fetch data from a Weather API on button click and when the data is fetched then that data should be mapped inside a component(i.e. Weather) and then only that component should appear on the screen, right now I am able to fetch the data but even then the component is not appearing.
Container.jsx
import React from 'react';
import './container.css'
import Weather from './weather';
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
location: "",
weather: []
};
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
componentDidMount() {
}
continue = (e) => {
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});
})
.catch(err => console.log("error ",err))
}
};
render() {
console.log(this.state);
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}><span class="fa fa-search"></span></button>
</label>
</div>
<div>
{weather}
</div>
</div>
);
}
}
export default Container;
Weather.jsx
import React from 'react';
class Weather extends React.Component {
render(){
return (
<div id="result">
<div id="location" class="insideres">
<div class="title">
Location
</div>
<div class="res">
{this.props.location}
</div>
</div>
<div id="Temperature" class="insideres">
<div class="title">
Temperature
</div>
<div class="res">
{this.props.temperature}
</div>
</div>
<div id="Weather" class="insideres">
<div class="title">
Weather
</div>
<div class="res">
{this.props.weather}
</div>
</div>
<div id="Windspeed" class="insideres">
<div class="title">
Wind Speed
</div>
<div class="res">
{this.props.windSpeed}
</div>
</div>
<div id="Wind_degree" class="insideres">
<div class="title">
Wind Degree
</div>
<div class="res">
{this.props.windDegree}
</div>
</div>
<div id="Wind_dir" class="insideres">
<div class="title">
Wind Direction
</div>
<div class="res">
{this.props.windDir}
</div>
</div>
<div id="Humidity" class="insideres">
<div class="title">
Humidity
</div>
<div class="res">
{this.props.humidity}
</div>
</div>
<div id="Visibility" class="insideres">
<div class="title">
Visibility
</div>
<div class="res">
{this.props.visibility}
</div>
</div>
</div>
);
}
}
export default Weather;
I want this weather component to appear when the data is fetched from the api, but right now data is being fetched but its not appearing.
In the above image you can see I am getting data from api, but not getting Weather component with that data under searchbar
Here is an update to your component using react with hooks. I highly suggest you adopt this pattern as it is way easier to work with, but does require using React 16 if you haven't adopted this yet. You will notice that I:
am using template strings instead of concatenating strings. This is best practice.
use async/await with promises
using an if statement to render the Weather component if the length of the weather variable in state is greater than 0. If it isn't, it will render the container component.
import "./container.css";
import React, { useState } from "react";
import Weather from "./weather";
const Container = () => {
const [location, setLocation] = useState("");
const [weather, setWeather] = useState([]);
const fetchWeatherData = async () => {
const url = `http://api.weatherstack.com/current?access_key=d8fefab56305f5a343b0eab4f837fec1&query=${location}`;
if (location.length < 1) {
return alert("Enter the details");
} else {
await fetch(url)
.then((response) => response.json())
.then((data) => {
setWeather(data);
})
.catch((err) => console.log("error ", err));
}
};
if (weather.length > 0) {
return 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}
/>
));
}
return (
<div id="container">
<div className="searchicon">
<input
placeholder="Enter City !!"
type="text"
name="location"
value={location}
onChange={(e) => setLocation(e.target.value)}
/>
<label className="icon">
<button onClick={fetchWeatherData}>
<span className="fa fa-search" />
</button>
</label>
</div>
<div>{weather}</div>
</div>
);
};
export default Container;
I have created some basic beginner React apps. But now I want to try using a template "core-ui".
https://github.com/coreui/coreui-free-react-admin-template
I would like to make requests to some external endpoints and retrieve some data but I'm not sure where to do it.
Here's what I've done on my own:
import React from 'react';
import RowCreator from './RowCreator';
class DisplayCountries extends React.Component {
constructor(props){
super(props);
this.state = {countries:[],
countriesClone:[]
};
}
componentDidMount() {
const axios = require('axios');
const url = 'http://localhost:8080/demo/api/countries';
axios.get(url).then(res=>{
console.log(res.data);
this.setState({countries:res.data,
countriesClone:res.data});
}).catch(error=>{
console.error('Error', error);
})
}
handleOnChange(event){
var filteredString = event.target.value;
var filteredCountries = [];
for(var country of this.state.countries){
if(country.cioc.toLowerCase().indexOf(filteredString.toLowerCase())>=0 ||
country.name.toLowerCase().indexOf(filteredString.toLowerCase())>=0 ||
country.capital.toLowerCase().indexOf(filteredString.toLowerCase())>=0 ||
country.region.toLowerCase().indexOf(filteredString.toLowerCase())>=0 ||
country.subregion.toLowerCase().indexOf(filteredString.toLowerCase())>=0 ){
filteredCountries.push(country);
}
}
this.setState({countriesClone:filteredCountries});
}
render(){
return (<div>
<div className="headerBox">
<div className="row">
<div className="col-sm-12 text-center">
<h1>Search Countries</h1>
</div>
</div>
<div className="row">
<div className="col-sm-12 text-center">
<h3>Demo to filter the list of countries</h3><br/>
</div>
</div>
</div>
<div className="searchBox">
<div className="row text-right">
<div className="col-sm-3"/>
<div className="col-sm-6 text-center">
<br/><input type="text" className="form-control input-lg" placeholder="Search any field by name" onChange={this.handleOnChange.bind(this)}/><br/>
</div>
<div className="col-sm-3"/>
</div>
</div>
<div className="container">
<div className="row">
<div className="col-sm-12"><br/>
<table className="table table-striped table-bordered">
<thead>
<tr>
<th>CIOC</th>
<th>Country</th>
<th>Capital</th>
<th>Region</th>
<th>Sub Region</th>
</tr>
</thead>
<tbody>
{this.state.countriesClone.map(country => <RowCreator item={country} key={country.cioc}/>)}
</tbody>
</table>
</div>
</div>
</div>
</div>
)}
}
export default DisplayCountries;
But when I review the view of the CoreUI page, I can't figure out where to add my constructor, etc. Any ideas
Here's an example of a .js file for one of the pages:
import React, { useState, useEffect } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import {
CBadge,
CCard,
CCardBody,
CCardHeader,
CCol,
CDataTable,
CRow,
CPagination
} from '#coreui/react'
import processesData from './ProcessData'
const getBadge = status => {
switch (status) {
case 'Active': return 'success'
case 'Inactive': return 'secondary'
case 'Pending': return 'warning'
case 'Banned': return 'danger'
default: return 'primary'
}
}
const Processes = () => {
const history = useHistory()
const queryPage = useLocation().search.match(/page=([0-9]+)/, '')
const currentPage = Number(queryPage && queryPage[1] ? queryPage[1] : 1)
const [page, setPage] = useState(currentPage)
const pageChange = newPage => {
currentPage !== newPage && history.push(`/processes?page=${newPage}`)
}
useEffect(() => {
currentPage !== page && setPage(currentPage)
}, [currentPage, page])
return (
<CRow>
<CCol xl={12}>
<CCard>
<CCardHeader>
<h4 id="process" className="card-title mb-0">Processes</h4>
</CCardHeader>
<CCardBody>
<CDataTable
items={processesData}
fields={[
{ key: 'id', _classes: 'font-weight-bold' },
'name', 'startDate', 'endDate'
]}
columnFilter
tableFilter
hover
sorter
striped
itemsPerPageSelect
itemsPerPage={5}
activePage={page}
clickableRows
onRowClick={(item) => history.push(`/process/${item.id}`)}
/>
</CCardBody>
</CCard>
</CCol>
</CRow>
)
}
export default Processes
I think you're getting confused because in core-ui page, a functional component is written which uses hooks. To read more about hooks, Please go through the official docs if you've not. https://reactjs.org/docs/hooks-intro.html
useState
useEffect
You can convert your class component into a functional component like this
const DisplayCountries = () => {
[countries, setCountries] = useState([]);
useEffect(() => {
const axios = require("axios");
const url = "http://localhost:8080/demo/api/countries";
axios
.get(url)
.then((res) => {
setCountries(res.data);
})
.catch((error) => {
console.error("Error", error);
});
}, []); // Empty array of dependency makes it equivalent to componentDidMount
return (<div/>) // render your element like you'd do in a class component
};
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