I am using React js. I have a class Stock.js where I am fetching an api and displaying the data on the webpage in the form of table.
When I click on the table data (table data are links) It sends the item.symbol to onhandleclick() method. For example:
|Symbol|Age|
|X | 20|
|Y |22 |
So the values in symbol table are referred as item.symbol
Here if I click on X it sends the value X to onhandleclick() and now I want to send this value X or Y whichever user clicks on to another class. By another class I mean let's say I have a class xyz.js I wanna send the value of item.symbol to class xyz.js so I can use this value and do whatever I want with that value in my xyz.js class. Is there a way to do it?
My code: (Stock.js)
import React, { Component } from "react";
import { Link } from "react-router-dom";
import Symbols from "./Symbols";
export default class Stocks extends Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
symbolsname: "",
};
}
handleClick(symbol) {
//pass the value to another class here
}
componentDidMount(symbol) {
fetch("http://131.181.190.87:3001/all")
.then((res) => res.json())
.then((json) => {
this.setState({
isLoaded: true,
items: json,
});
});
}
render() {
let filteredItems = this.state.items.filter((item) => {
return (
item.symbol.toUpperCase().indexOf(this.state.search.toUpperCase()) !==
-1 || item.industry.indexOf(this.state.search) !== -1
);
});
var { isLoaded, items } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div>
<table border={2} cellPadding={1}>
<thead>
<tr>
<th>Symbol</th>
<th>Name</th>
<th>Industry</th>
</tr>
</thead>
<tbody>
{filteredItems.map((item) => (
<tr>
<Link to="/symbols">
<td
key={item.symbol}
onClick={() => this.onhandleclick(item.symbol)} //here I am passing the value of item.symbol to onhandleclick()
>
{item.symbol}
</td>
</Link>
<td key={item.name}>{item.name}</td>
<td key={item.industry}>{item.industry}</td>
</tr>
))}
}
</tbody>
</table>
</div>
);
}
}
}
After doing what maniraj-murugansaid in the answers, it says undefined, so I have uploaded the screenshot
You could redirect to symbol.js using history.push with click event handler like, (Remove Link tag here) So change,
<Link to="/symbols">
<td key={item.symbol} onClick={() => this.onhandleclick(item.symbol)} //here I am passing the value of item.symbol to onhandleclick()>
{item.symbol}
</td>
</Link>
to,
<td key={0} onClick={() => this.onhandleclick(item.symbol)}
style={{ cursor: "pointer", color: "blue" }}
>
{item.symbol}
</td>
And onHandleClick function like,
onhandleclick(data) {
const { history } = this.props;
history.push({
pathname: "/Symbol",
symbol: data
});
}
Here the second property is props that you can pass which is symbol in your case so you can give it like, symbol: data ..
Working Sandbox: https://codesandbox.io/s/react-router-v4-withrouter-demo-2luvr
Update:
-> After the update from OP , there are some changes that have been made.
=> import { BrowserRouter } from "react-router-dom"; in the main component index.js where you are initializing the parent component in the call to ReactDOM.render .
index.js:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
const rootElement = document.getElementById("root");
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
rootElement
);
stocks.js:
import React, { Component } from "react";
import { Link } from "react-router-dom";
import Symbols from "./Symbols";
const filteredItems = [
{ symbol: "X", name: "item1", industry: "industry1" },
{ symbol: "Y", name: "item2", industry: "industry2" }
];
export default class Stocks extends Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
search: "",
symbol: ""
};
}
updateSearch(event) {
this.setState({ search: event.target.value });
}
onhandleclick(data) {
const { history } = this.props;
history.push({
pathname: "/Symbols",
symbol: data
});
}
componentDidMount() {}
render() {
return (
<div>
<form className="form-for-table-search">
Search symbol or industry:
<input
type="text"
value={this.state.search}
onChange={this.updateSearch.bind(this)}
/>
{" "}
<button type="button" className="btn-submit">
Search
</button>
<br />
</form>
<table border={2} cellPadding={1}>
<thead>
<tr>
<th>Symbol</th>
<th>Name</th>
<th>Industry</th>
</tr>
</thead>
<tbody>
{filteredItems.map((item, index) => (
<tr key={index}>
<td
key={0}
onClick={() => this.onhandleclick(item.symbol)} //here I am passing the value of item.symbol to onhandleclick()
style={{ cursor: "pointer", color: "blue" }}
>
{item.symbol}
</td>
<td key={item.name}>{item.name}</td>
<td key={item.industry}>{item.industry}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
Symbols.js:
import React from "react";
export default class Symbol extends React.Component {
componentDidMount() {
console.log("came here", this.props.location.symbol);
}
render() {
return <div>Symbol value: {this.props.location.symbol}</div>;
}
}
Updated Sandbox
You could export a function from Symbol.js and use that in handleClick.
// Symbol.js
export default class Symbol {
doSomething(symbol) {
// do something
}
}
// Stocks.js
import Symbol from 'Symbol.js';
handleClick(symbol) {
Symbol.doSomething(symbol);
};
Related
I have a react component called productsTable where a user can specify the number of products they want for an item and then add it to their cart. I am using a input field to allow them to enter the number of products. The issue I am having is that if a user enters the values for the input fields and clicks add to cart for each product, the value that setState will have is the last value entered in the input field vs having that individual input fields value. The reason this happens is the way I am using setState which is updated in the onChange of the input. Any idea on what I can do to fix this. I was thinking maybe putting the input as it's own seperate component so each product can get it's own instance of an input. Code is down below.
import { Table, Button } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { useEffect, useState } from 'react';
import { getAllProducts } from '../../redux/actions/productActions';
import { sortBy } from 'lodash';
import { toast } from 'react-toastify';
import { addToCart } from '../../redux/actions/shoppingCartActions';
const ProductsTable = ({ searchTerm }) => {
const userInfo = JSON.parse(localStorage.getItem('userInfo')) || {};
const dispatch = useDispatch();
const [input, setInput] = useState(0);
const cartId = useSelector((state) => state.cartStatus.cartInfo.cartId);
const handleAdd = (product, input) => {
const isBadInput = validateInput(input);
if (isBadInput) {
toast.error('Invalid product amount', {
position: toast.POSITION.TOP_CENTER,
autoClose: 1200,
});
return;
}
const myProduct = {
productAmount: input,
...product,
userId: userInfo.userId,
cartId: cartId,
};
dispatch(addToCart(myProduct));
toast.success('Product added successfuly', {
position: toast.POSITION.TOP_CENTER,
autoClose: 1500,
});
};
useEffect(() => {
dispatch(getAllProducts());
}, []);
const products = useSelector((state) => state.productReducer.products);
const sortedProducts = sortBy(products, ({ productName }) =>
productName.toLowerCase()
);
const validateInput = (inputValue) => {
let value = parseInt(inputValue, 10);
if (value < 0) {
return true;
} else {
value = value >= 0 ? value : 0;
setInput(value);
}
};
return (
<div className='col-9'>
<Table striped bordered hover>
<thead>
<tr>
<th>Product Name</th>
<th>Product Price</th>
<th>Seller</th>
<th>Amount In Stock</th>
<th>Amount To Purchase</th>
<th></th>
</tr>
</thead>
<tbody>
{sortedProducts.length > 0 &&
sortedProducts
.filter((product) => {
const { productName } = product;
if (searchTerm === '') {
return product;
}
if (productName.toLowerCase().includes(searchTerm)) {
return product;
}
})
.map((product) => {
return (
<tr key={product.id}>
<td>{product.productName}</td>
<td>{product.productPrice}</td>
<td>Seller </td>
<td>{product.productInStock}</td>
<td>
<input
type='number'
name='input'
step='1'
onChange={(event) => setInput(event.target.value)}
/>
</td>
<td>
<Button
onClick={() => handleAdd(product, input)}
variant='primary'
>
Add To Cart
</Button>
</td>
</tr>
);
})}
</tbody>
</Table>
</div>
);
};
export default ProductsTable;
For those of you who run into a similar issue, I moved the input field into its own component and called it in the productsTable component. That way there is a separate input field for each product. I had a feeling that would work originally but after trying on my own, I was able to prove it.
I am new to react js and i am designing form where user enters name and on add button click the data is showed in table.On edit button the data of selected row should be showed in input of form and update the values on add button click. i am unable to code the logic for the same .It would be of great help if anyone would provide solution for the update operation.Below is my coding
App.js
import React, { Component } from 'react';
import Table from './Table';
import Form from './Form';
import './App.css';
class App extends React.Component {
constructor() {
super();
this.state = {
id: 1,
firstname: '',
items: []
}
};
handleFormSubmit = (e) => {
e.preventDefault();
let items = [...this.state.items];
items.push({
id: this.state.id,
firstname: this.state.firstname,
});
this.setState({
items,
id: this.state.id + 1,
firstname: '',
});
};
handleInputChange = (e) => {
let input = e.target;
let name = e.target.name;
let value = input.value;
this.setState({
[name]: value
})
};
render() {
return (
<div className = "App" >
<Form handleFormSubmit = { this.handleFormSubmit }
handleInputChange = { this.handleInputChange }
newId = { this.state.id }
newFirstname = { this.state.firstname }/>
<Table items = { this.state.items }/>
</div >
);
}
}
export default App;
Table.js
import React, { Component } from 'react';
import './App.css';
class Table extends React.Component {
render() {
const items = this.props.items;
return (
<div id = "Table" >
<table class = "tdgreeting" border = "1" frame = "void" rules = "rows" >
<tbody >
<tr >
<th > Id < /th>
<th > FirstName < /th>
<th > Edit < /th>
< /tr >
{
items.map(item => {
return (
<tr >
<td > { item.id } < /td>
<td > { item.firstname} < /td>
<td > < button class = "btnStyle" onClick = { this.props.onUpdate } > Edit < /button></td >
</tr>
);
})
}
</tbody>
</table>
</div>
);
}
}
export default Table;
Form.js
import React, { Component } from 'react';
import './App.css';
class Form extends React.Component {
render() {
return (
<div class = "main" >
<h3 > Greetings < /h3>
<form id = "formInput" onSubmit = { this.props.handleFormSubmit } >
<input id = "firstname"
value = { this.props.newFirstname }
type = "firstname"
name = "firstname"
placeholder = "Firstname"
onChange = { this.props.handleInputChange }
required / >
<button type = "submit"
value = "Submit" > Save < /button>
<button type = "reset"
value = "Reset" > Cancel < /button>
< /form >
< /div >
);
}
}
export default Form;
I was able to achieve what you mentioned in the question. The whole code could be refactored a lot if you're using Hooks since that is beyond the scope of this question, we can update our class-based component.
I added a new onUpdate function handler which receives the edited name from the Table component and updates your existing items state with the matching id received from the Table Component.
Here is a working sandbox.
//App.js
import React from "react";
import Table from "./Table";
import Form from "./Form";
class App extends React.Component {
constructor() {
super();
this.state = {
id: 1,
firstname: "",
items: [],
};
}
handleFormSubmit = (e) => {
e.preventDefault();
let items = [...this.state.items];
items.push({
id: this.state.id,
firstname: this.state.firstname,
});
this.setState({
items,
id: this.state.id + 1,
firstname: "",
});
};
handleInputChange = (e) => {
let input = e.target;
let name = e.target.name;
let value = input.value;
this.setState({
[name]: value,
});
};
onUpdate = (item) => {
const updatedData = this.state.items.map((x) =>
x.id === item.id ? { ...x, firstname: item.newFirstname } : x
);
this.setState({ items: updatedData });
};
render() {
return (
<div className="App">
<Form
handleFormSubmit={this.handleFormSubmit}
handleInputChange={this.handleInputChange}
newId={this.state.id}
newFirstname={this.state.firstname}
/>
<Table items={this.state.items} onUpdate={this.onUpdate} />
</div>
);
}
}
export default App;
Added a separate form-inputs in your Table component to avoid it being tightly coupled with your App.js file. Of course, you could refactor this a lot using hooks and with proper usage of components.
//Table.js
import React from "react";
import Form from "./Form";
class Table extends React.Component {
state = {
isEdit: false,
newFirstname: "",
id: "",
};
updateItem = (item) => {
this.setState({ isEdit: true, id: item.id });
};
handleInputChange = (e) => {
this.setState({ newFirstname: e.target.value });
};
handleFormSubmit = (e) => {
e.preventDefault();
this.props.onUpdate(this.state);
this.setState({ isEdit: false });
};
render() {
const items = this.props.items;
return (
<div id="Table">
<table class="tdgreeting" border="1" frame="void" rules="rows">
<tbody>
<tr>
<th> Id </th>
<th> FirstName </th>
<th> Edit </th>
</tr>
{items.map((item) => {
return (
<tr>
<td> {item.id} </td>
<td> {item.firstname} </td>
<td>
{" "}
<button
class="btnStyle"
onClick={() => this.updateItem(item)}
>
{" "}
Edit{" "}
</button>
</td>
</tr>
);
})}
</tbody>
</table>
{this.state.isEdit ? (
<Form
handleFormSubmit={this.handleFormSubmit}
handleInputChange={this.handleInputChange}
/>
) : null}
</div>
);
}
}
export default Table;
I have a problem when I develop a react web application. Here's my code:
class TableContentRow extends React.Component {
render(){
return(
<tr>
<td>{this.props.voucher.merchantName}</td>
<td>{this.props.voucher.voucherCode}</td>
<td>{this.props.voucher.orderId}</td>
<td>{this.props.voucher.deal}</td>
<td>{this.props.voucher.dealDescription}</td>
<td>{this.props.voucher.price}</td>
<td>{this.props.voucher.redemptionStatus}</td>
<td>{this.props.voucher.redemptionTimestamp}</td>
</tr>
);
}
}
class TableContent extends React.Component {
render() {
const rows = [];
this.props.vouchers.forEach((voucher) => {
if(voucher.orderId.indexOf(this.props.filterText) === -1){return;}
rows.push(<TableContentRow voucher = {voucher} key = {voucher.orderId} />);
})
return(
<div className="panel panel-primary">
<div className="panel-heading">
<h3 className="panel-title">
All Vouchers
</h3>
</div>
<table className="table table-striped">
<thead>
<tr>
<th>Restaurant</th>
<th>Voucher Code</th>
<th>Order ID</th>
<th>Deal</th>
<th>Deal Description</th>
<th>Sale Price</th>
<th>Redemption Status</th>
<th>Redemption Timestamp</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
);
}
}
class VoucherAll extends React.Component {
constructor(props){
super(props);
this.handleFilterTextInput = this.handleFilterTextInput.bind(this);
this.loadVouchersFromServer = this.loadVouchersFromServer.bind(this);
this.state = {filterText: ''};
}
handleFilterTextInput(filterText) {
this.setState({
filterText: filterText
});
}
loadVouchersFromServer() {
$.ajax({
url: this.props.url,
success: function(data) {
this.setState({
data: data
});
},
error: function(xhr,status,err) {
console.log(this.props.url, status, err.toString());
}
})
}
componentDidMount() {
this.loadVouchersFromServer();
setInterval(this.loadVouchersFromServer, this.props.pollInterval);
}
render(){
return(
<div className="container">
<TableContent
vouchers = {this.state.data}
filterText = {this.state.filterText}
/>
</div>
);
}
}
ReactDOM.render(
<VoucherAll url = "voucher.json" pollInterval = {2000} />,
document.getElementById('voucherAll')
)
And here's my json file:
{
"merchantName":"xxxx",
"voucherCode":"xxxx",
"orderId":"xxxx",
"deal":"xxxx",
"dealDescription":"xxxx",
"price":"xxxx",
"redemptionStatus":"xxxx",
"redemptionTimestamp":"xxxx-xx-xx"
}
When I run my code, the web page shows nothing. And in the console, I cannot find any relative message. Can anyone help me to figure that out? Thanks.
You are loosing context inside ajax callbacks. Though loadVouchersFromServer is binded success and error callbacks aren't. You could use arrow functions or bind those callbacks.
loadVouchersFromServer() {
$.ajax({
url: this.props.url,
success: data => {
this.setState({
data: data
});
},
error: function(xhr,status,err) {
console.log(this.props.url, status, err.toString());
}.bind(this)
})
}
My App.jsx file is below.
import React from 'react';
class App extends React.Component {
constructor() {
super();
this.state = {
data:require('json!./dataa.json')
}
}
render() {
return (
<body>
<div>
<Header/>
<center>
<table>
<tr><th>NAME</th><th>VALUE</th><th>COLOR</th><th>Edit Table</th></tr>
<tbody>
{this.state.data.table.map(person, i) => <TableRow key = {i} data = {person} />)}
</tbody></table></center>
</div>
</body>
);
}
}
class Header extends React.Component {
render() {
return (
<div><center>
<h1>Creation of table from JSON</h1></center>
</div>
);
}
}
class TableRow extends React.Component {
render() {
return (
<tr>
<td>{this.props.data.NAME}</td>
<td>{this.props.data.VALUE}</td>
<td>{this.props.data.COLOR}</td>
<td contentEditable='true'></td>
</tr>
);
}
}
export default App;
and my dataa.json file is like below
[{"table":
[{"NAME":"Alan","VALUE":12,"COLOR":"blue"},
{"NAME":"Shan","VALUE":13,"COLOR":"green"},
{"NAME":"John","VALUE":45,"COLOR":"orange"},
{"NAME":"Minna","VALUE":27,"COLOR":"teal"}]
}]
Question: It is compiled fine. but it display error in browser "cannot read the property of map undefined".How to resolve
Note: but it works fine when the json file like,
[{"NAME":"Alan","VALUE":12,"COLOR":"blue"},
{"NAME":"Shan","VALUE":13,"COLOR":"green"},
{"NAME":"John","VALUE":45,"COLOR":"orange"},
{"NAME":"Minna","VALUE":27,"COLOR":"teal"}]
}]
this.state.data doesn't have property table, because it is an array of single object.
Correct JSON structure to this
{
"table": [
{"NAME":"Alan","VALUE":12,"COLOR":"blue"},
{"NAME":"Shan","VALUE":13,"COLOR":"green"},
{"NAME":"John","VALUE":45,"COLOR":"orange"},
{"NAME":"Minna","VALUE":27,"COLOR":"teal"}
]
}
and use this.state.data.table.map.
I am receiving json data from server and i am trying to access it individually using map function in front end but i am receiving an error as this.state.post.map is not a function. below is the code how do i overcome this.
import React from 'react';
import axios from 'axios';
//require('./style.scss');
class Premontessori extends React.Component{
constructor(props){
super(props);
this.state={
post:[]
};
}
componentDidMount(){
let self = this;
axios.get('http://localhost:8080/list')
.then(function(data) {
console.log(data);
self.setState({post:data});
});
}
render(){
return (
<div>
<table>
<tbody>
{
this.state.post.map(function(item, index){
return (
<tr>
<td>{item.Id}</td>
<td>{item.Name}</td>
<td>{item.Age}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
);
}
}
export default Premontessori;
From your comment,
'since this.state.post is not an array hence you get an error that map is not a function.
You need to map over the data in the post object like
<tbody>
{this.state.post.data.map(function(item, index) {
return (
<tr key={index}>
<td>{item.Id}</td>
<td>{item.Name}</td>
<td>{item.Age}</td>
</tr>
)
})
}
</tbody>
I think you have to make an adjustment in the code in the componentDidMount life-cycle method as shown below. This is because the response from the axios call is wrapping the array data, so you have to fetch that array and update your state if the status code is 200.
import React from 'react';
import axios from 'axios';
//require('./style.scss');
class Premontessori extends React.Component{
constructor(props){
super(props);
this.state={
post:[]
};
}
componentDidMount(){
let self = this;
axios.get('http://localhost:8080/list')
.then(function(res) {
console.log(res);
if(res.status === 200){
self.setState({post:res.data});
}
})
.catch(function(err){
console.log(err);
});
}
render(){
return (
<div>
<table>
<tbody>
{
this.state.post.map(function(item, index){
return (
<tr>
<td>{item.Id}</td>
<td>{item.Name}</td>
<td>{item.Age}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
);
}
}
export default Premontessori;