I am trying to make a an accordion using React but and I receive my data from a Json API which I dynamically use to make the table. All that works.
However when I try to make the toggleHidden function, it opens every single view instead of the one I just clicked on. How can I target a specific component when clicking instead of just using this
<Table striped bordered condensed hover>
<thead>
<tr>
<th>Asset #</th>
<th>Description</th>
<th>Person</th>
<th>Username</th>
<th>Room</th>
<th>ID</th>
<th>Patches</th>
<th>Date</th>
<th>Java</th>
<th>AACV</th>
</tr>
</thead>
{this.state.items.map((data, key) => {
return (
<tbody>
<tr
id={data.name}
onClick=`{this.toggleHidden.bind(this)}
>`
<td>{data.name}</td>
<td >{data.height}</td>
<td>{data.mass}</td>
<td>{data.gender}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
</tr>
<td
className={data.name}
colSpan="10"
>
{!this.state.isHidden && <Filled/>}
</td>
</tbody>
)
})}
</Table>
toggleHidden (e) {
this.setState({
isHidden: !this.state.isHidden
})
}
I Would suggest you to use the power of components.
Create component for row inside the table, something like this:
export default class Row extends React.Component {
render() {
const { data } = this.props;
<tbody>
<tr
id={data.name}
onClick=`{this.toggleHidden.bind(this)}
>`
<td>{data.name}</td>
<td >{data.height}</td>
<td>{data.mass}</td>
<td>{data.gender}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
</tr>
<td
className={data.name}
colSpan="10"
>
{!this.state.isHidden && <Filled/>}
</td>
</tbody>
}
toggleHidden() {
this.setState(state => ({
isHidden: !state.isHidden
}));
}
}
and in your main component do something like this:
<Table striped bordered condensed hover>
<thead>
<tr>
<th>Asset #</th>
<th>Description</th>
<th>Person</th>
<th>Username</th>
<th>Room</th>
<th>ID</th>
<th>Patches</th>
<th>Date</th>
<th>Java</th>
<th>AACV</th>
</tr>
</thead>
{this.state.items.map((data, key) => <Row data={data} />}
</Table>
it's more readable, it's more react style, and it's let each row to handle their own state
(there might be some syntax error, but the concept is working)
Good Luck!
Well you need to make nested state for each key that means
const { Table, Icon } = semanticUIReact;
const ITEMS = [
{
asset: "Asset 1",
description: "Description"
},
{
asset: "Asset 2",
description: "Description2"
}
];
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { items: ITEMS };
this.renderRow = this.renderRow.bind(this);
}
render() {
return (
<Table striped bordered condensed selectable>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Asset</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>{this.state.items.map(this.renderRow)}</Table.Body>
</Table>
);
}
renderRow(data, key) {
return (
<Table.Row onClick={() => this.toggleHidden(key)}>
<Table.Cell>{data.asset}</Table.Cell>
<Table.Cell textAlign="right">
{this.state[key] ? "This is hidden" : data.description}
</Table.Cell>
</Table.Row>
);
}
toggleHidden(key) {
this.setState({
[key]: !this.state[key]
});
}
}
ReactDOM.render(<Example />, document.getElementById("react"));
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/semantic-ui-react#0.79.1/dist/umd/semantic-ui-react.min.js"></script>
<div id="react"></div>
Related
I have passed an array of objects in useState hook as the initial state and i want to iterate over it using map method in other component, i have passed the current state as prop in the other object and have used map method over it but console message still shows values.map is not a function, i know we can only use map method on array i have done that only still the error shows.
created state and passed an array of objects-
const [values, setValues] = useState([
{
name: "Vansh",
age: 22,
email: "vansh#gmail.com",
},
]);
Passed values as props in Home component-
<Home
addFormData={addFormData}
setAddFormData={setAddFormData}
values={values}
setValues={setValues}
/>
Code of my home component-
const Home = ({ values }) => { return (
<div>
<table>
<thead>
<tr>
<th>name</th>
<th>age</th>
<th>email</th>
</tr>
</thead>
<tbody>
{values.map((val, index) => (
<tr>
<td key={val.index}>{val.name}</td>
<td key={val.index}>{val.age}</td>
<td key={val.index}>{val.email}</td>
</tr>
))}
</tbody>
</table>
<Link to="/form">
<button>FORM</button>
</Link>
</div> ); };
Please see the code it's working fine export below your missing export.
const Home = ({ values }) => {
// console.log(values);
return (
<div>
<table>
<thead>
<tr>
<th>name</th>
<th>age</th>
<th>email</th>
</tr>
</thead>
<tbody>
{values.map((val, index) => (
<tr>
<td key={val.index}>{val.name}</td>
<td key={val.index}>{val.age}</td>
<td key={val.index}>{val.email}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default Home;
Please see the code SandBoxLink
I am successfully returning data from my axios call on page load.
constructor(props) {
super(props);
this.state = {
myData: []
}
}
componentDidMount() {
let context= this;
axios.get('http://localhost:3000/returntestvalues',{
})
.then((res) => {
console.log("Res os: ", res.data)
context.setState({
myData: res.data
});
})
console.log("res.data", this.state.myData)
}
As you can see there, I am setting the State to the returned data. Now, I want to output this into a table:
render() {
return (
<body onload="renderData()">
<table id="customers">
<tr>
<th>Name</th>
<th>Email</th>
<th>Message</th>
</tr>
<tr>
{this.state.myData?.length > 0
? this.state.myData.map((data, index) => (
<tr key={index}>
<td>{data.name}</td>
<td>{data.email}</td>
<td>{data.message}</td>
</tr>
))
: "No Data Found"}
</tr>
</table>
</body>
);
}
}
however, I don't know how to do this through react, I've only ever done it through ejs. Can anyone help?
render() {
return (
<body onload="renderData()">
<table id="customers">
<tr>
<th>Name</th>
<th>Email</th>
<th>Message</th>
</tr>
<tr>
{this.state.myData?.length > 0
? this.state.myData.map((data, index) => (
<React.Fragment key={index}>
<td>{data.name}</td>
<td>{data.email}</td>
<td>{data.message}</td>
</React.Fragment>
))
: "No Data Found"}
</tr>
</table>
</body>
);
}
}
Here i am assuming that the keys are name email and message. you can change yours.
You can map your received data like below.
return (
<body onload="renderData()">
<table id="customers">
<tr>
<th>Name</th>
<th>Email</th>
<th>Message</th>
</tr>
{this.state.myData?.length>0 && this.state.myData.map(function(data,index){
return (<tr>
<td>{data.name}</td>
<td>{data.email}</td>
<td>{data.message}</td>
</tr>);
})}
</table>
</body>
);
I have a question about the component panel in react
Currently, I have a requirement to create a common component for the Order and Product tables. But the two tables have different numbers of columns, one side has many columns and one side has few columns, besides there are also different table names.
I have a piece of code like this
import React from 'react';
import {Table, Image} from 'react-bootstrap';
import '../Table/index.css';
import Button from '../Button/index';
const TableItem = ({productList}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
<th>No. </th>
<th>Image</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{productList.map((product, index) => (
<tr key={index}>
<td>{product.id}</td>
<td><Image src={product.image} /></td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>
<Button variant="success" onClick={redirectToEdit}>Edit</Button>
<Button variant="danger" onClick={deleteProductItem}>Delete</Button>
</td>
</tr>
))}
</tbody>
</Table>
);
}
export default TableItem;
That code I have created for the product table, but the order table is not because I do not know how to do it properly. I named fixed for the column, I know this is wrong because the order table will also retrieve components from this table, so I can not make the name like that
The order table also has the following columns: Username, address, quantity, status .....
How can I change the code in this component that can be used for both tables
Can anyone help me to explain for this, thank you so much
So you can do something like this, create a table component and pass the columns list and data in there as props that way you can control the table from the parents component and can be used in any way as you want.
const TableItem = ({data, columns}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
{
columns.map((column, index) => {
<th key={ index }>{ column.name }</th>
}
}
</tr>
</thead>
<tbody>
{data.map((product, index) => (
<tr key={index}>
<td>{product.id}</td>
<td><Image src={product.image} /></td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>
<Button variant="success" onClick={redirectToEdit}>Edit</Button>
<Button variant="danger" onClick={deleteProductItem}>Delete</Button>
</td>
</tr>
))}
</tbody>
</Table>
);
}
and in the parent component you can do something like
const parentComponent = () => {
return {
<TableItem columns={ productColumns } data={ productData } />
<TableItem columns={ orderColumns } data={ orderData }
}
}
Note Code not tested and is pseudo
You can pass columns (Array) as a props and render it.
const columns = ['username', 'quantity', 'etc'];
const TableItem = ({List, columns}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
{columns.map((name, index) => (
<th key={index}>{name}</th>
))}
</tr>
</thead>
<tbody>
</Table>
)
})
If you have some common columns then you can pass one more props commonColumns like that.
const TableItem = ({List, columns, commonColumns}) => {
return(
<Table striped bordered hover>
<thead>
<tr>
{commonColumns.map((name, index) => (
<th key={index}>{name}</th>
))}
{columns.map((name, index) => (
<th key={index}>{name}</th>
))}
</tr>
</thead>
<tbody>
</Table>
)
})
Hope this will help you.
I am trying to render a list of movies from an array object in an html table format. I am getting this warning:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
import React from 'react';
import {movies} from '../services/fakeMovieService';
class Movies extends React.Component {
constructor(props) {
super(props);
this.tableFormat = this.tableFormat.bind(this);
}
tableFormat() {
movies.map((movie) => {
return (
<tr>
<td key={movie._id}>{movie.title}</td>
</tr>
);
});
}
render() {
return (
<table className="table">
<thead>
<tr>
<th>Title</th>
<th>Genre</th>
<th>Stock</th>
<th>Rate</th>
</tr>
</thead>
<tbody>
{this.tableFormat()}
</tbody>
</table>
);
}
}
export default Movies;
You forgot to call your function.
<tbody>
{this.tableformatter()}
</tbody>
But even by doing, I don't think the result is going to be what you expect.
To render an array of elements in React you should use the map function as said in the docs.
The following result would be :
<tbody>
{movies.map(movie =>
<tr key={movie.title}>
<td>{movie.title}</td>
</tr>
)}
</tbody>
EDIT:
I made a typo and put movies instead of movie.
The following code should do everything you are looking for using map and inline conditions:
const movies = [
{
title: "Spooky",
genre: 'eziojgf',
stock: 'nope',
rate: 87
},
{
title: "Sharknado",
genre: 'shitty',
stock: 'yes',
},
{
title: "haha yes"
},
{
title: "uhmmmm",
rate: -5
}
]
class Movies extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<table className="table">
<thead>
<tr>
<th>Title</th>
<th>Genre</th>
<th>Stock</th>
<th>Rate</th>
</tr>
</thead>
<tbody>
{movies.map(movie =>
<tr key={movie.title}>
{['title', 'genre', 'stock', 'rate'].map(category => <td key={category}>{movie[category]}</td>)}
</tr>
)}
</tbody>
</table>
);
}
}
ReactDOM.render(<Movies />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.1/umd/react-dom.production.min.js"></script>
<div id='root'>
Please execute function as following:
<tbody>
{this.tableformatter()}
</tbody>
here is the right way how to resolve this with React
function TableFormatter ({title /* , and other props genre, rate etc. *//}) {
return (
<tr>
<td>{title}</td>
{/* you may want to add other td here *//}
</tr>
)
}
function Table({movies}) {
render() {
return (
<table className="table">
<thead>
<tr>
<th>Title</th>
<th>Genre</th>
<th>Stock</th>
<th>Rate</th>
</tr>
</thead>
<tbody>
{movies.map(movie => <TableFormatter key={movie.id} {...movie} />)}
</tbody>
</table>
);
}
}
I am tryng to display search results for each key press on my input:
getMovies(e){
axios.get(`http://www.omdbapi.com/?t=${e.target.value}`)
.then((response) => {
this.setState({ movies: response.data });
})
.catch((error) => {
console.log(error);
});
}
render() {
return (
<div className="container">
<SearchForm onkeydown={this.getMovies} />
<MovieList movies={this.state.movies}/>
</div>
);
}
}
In my search form I bind my function to the FormControl onChange:
export default class SearchForm extends React.Component{
render(){
return(
<Row>
<Col md={6} >
<h2>Custom search field</h2>
<div className="custom-search-input">
<Col md={12} className="input-group" >
<FormControl
type="text"
bsSize="lg"
value={this.props.val}
placeholder="Enter text"
onChange={this.props.onkeydown.bind(this)}
/>
<span className="input-group-btn">
<button className="btn btn-lg" type="button">
<i className="glyphicon glyphicon-search"></i>
</button>
</span>
</Col>
</div>
</Col>
</Row>)
}
}
My movielist component:
export default class MovieList extends React.Component{
render(){
var userNodes = this.props.movies.map(function(movie){
return (
<tr key={movie.id}>
<td>{movie.Year}</td>
<td >{movie.Title}</td>
<td >{movie.Released}</td>
</tr>
)
});
return (
<div>
<Table responsive>
<thead>
<tr>
<th>id</th>
<th>Year</th>
<th>Title</th>
<th>Released</th>
</tr>
</thead>
<tbody>
{userNodes}
</tbody>
</Table>
</div>
);
}
}
I can get the response on the network panel, but the state is not updating to display the MovieList component.
How can I update the state and display it in my MovieList ?
I've checked that API and seems it returns an object, not an array.
Update 1
You can convert your component in something like:
export default class MovieList extends React.Component{
render(){
const { movie } = this.props;
return (
<div>
<Table responsive>
<thead>
<tr>
<th>Id</th>
<th>Year</th>
<th>Title</th>
<th>Released</th>
</tr>
</thead>
<tbody>
<tr>
<td>{movie.imdbID}</td>
<td>{movie.Year}</td>
<td>{movie.Title}</td>
<td>{movie.Released}</td>
</tr>
</tbody>
</Table>
</div>
);
}
}
and use it like:
<MovieList movie={this.state.movies} />
Please notice I'm using movie instead of movies.
Update 2:
You can also (and I would suggest doing this) convert your MovieList into a dumb functional component:
const MovieList = ({ movie }) => (
<div>
<Table responsive>
<thead>
<tr>
<th>Id</th>
<th>Year</th>
<th>Title</th>
<th>Released</th>
</tr>
</thead>
<tbody>
<tr>
<td>{movie.imdbID}</td>
<td>{movie.Year}</td>
<td>{movie.Title}</td>
<td>{movie.Released}</td>
</tr>
</tbody>
</Table>
</div>
)
export default MovieList;