Im sending in an array via props to a component, but i cant use map on the array. It says that
.map is not a function
My code:
const AuthorList = (authors) => {
return(
<table className="table">
<thead>
<tr>Firstname</tr>
<tr>Lastname</tr>
</thead>
<tbody>
{authors.map(author =>
<AuthorListRow key={author.id} author={author} />
)}
</tbody>
</table>
);
};
This is what the Chrome React dev tool looks like:
Issue is, whenever you pass any data from parent to child, it get passed through props, and you need to receive the props in child component and access the specific values, write it like this:
const AuthorList = (props) => {
return(
<table className="table">
<thead>
<tr>Firstname</tr>
<tr>Lastname</tr>
</thead>
<tbody>
{props.authors.map(author =>
<AuthorListRow key={author.id} author={author} />
)}
</tbody>
</table>
);
};
or
const AuthorList = ({authors}) => {
return(
<table className="table">
<thead>
<tr>Firstname</tr>
<tr>Lastname</tr>
</thead>
<tbody>
{authors.map(author =>
<AuthorListRow key={author.id} author={author} />
)}
</tbody>
</table>
);
};
Reason why 2nd one is working: because props is an object, when you write {authors} it means you are receiving only authors value from object props. In that case you don't need to write props.authors.
Check this example:
obj = {a:1,b:2,c:3}
let {a} = obj;
console.log(a);
Props will be passed in as an object, so right now authors is acting as an alias for props. Accessing the authors property on props should work as long as that prop is being declared with an array.
const AuthorList = (props) => {
return(
// ..
{props.authors.map(author =>
<AuthorListRow key={author.id} author={author} />
)}
// ..
);
};
Related
I am currently trying to develop a table dashboard for a list of properties for the owner to see. And the issue that I'm having is that when I'm trying to display the tenant information, some of the properties don't have tenants(the data called rentalInfo in the JSON is "undefined") and I'm trying to conditionally render when the property does have a tenant, only then I display the tenant's name.
Here's the code for my Table:
import './table.css'
export default function Table(props){
const properties = props.data;
console.log(properties[4].rentalInfo[0].tenant_first_name,typeof(properties[4].rentalInfo[0].tenant_first_name))
const fourth_tenant = properties[4].rentalInfo[0].tenant_first_name
const rows = properties.map((row, index)=>{
return(
<tr>
<th>{row.rentalInfo.tenant_first_name !== 'undefined'?fourth_tenant:"No tenant at the moment"}</th>
</tr>
)
})
return(
<div>
<table>
<thead>
<tr>
<th>Tenant</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
)
}
Here's the code where I call the Table Function along with my Axios.get call
export default function OwnerDashboard2(){
const [dataTable, setDataTable] = useState([]);
useEffect(() => {
axios.get("https://t00axvabvb.execute-api.us-west-1.amazonaws.com/dev/propertiesOwner?owner_id=100-000003")
.then(res => {setDataTable(res.data.result)})
.catch(err => console.log(err))
}, []);
return(
<div className="OwnerDashboard2">
<Row>
<Col xs={2}><Navbar/></Col>
<Col>{dataTable.length!==0 && <Table data ={dataTable}/>}</Col>
</Row>
</div>
)
}
I tried using <th>{row.rentalInfo.tenant_first_name !== 'undefined'?fourth_tenant:"No tenant at the moment"}</th> but the issue is it's still displaying the only tenant in all the properties(fourth_tenant) for each column.
you can use !! to convert the value to boolean type
just like this:
!!row.rentalInfo.tenant_first_name ? row.rentalInfo.tenant_first_name : "tenant is undefined"
in my app.js
in const APP
i am returning
<Display results = {(good,neutral,bad)}></Display>
which are all numbers
in my display component which i have as this
const Display = ({good,neutral,bad}) => (
the parameters becomes undefined, or any. how come the numbers are not being passed.
full code
import { useState } from 'react'
const App = () => {
// save clicks of each button to its own state
const [good, setGood] = useState(0)
const [neutral, setNeutral] = useState(0)
const [bad, setBad] = useState(0)
return (
<div>
<h1>give feedback</h1>
<button onClick={() => setGood(good +1)}>Good </button>
<button onClick={() => setNeutral(neutral +1)}>Neutral</button>
<button onClick={() => setBad(bad +1)}>Bad</button>
<h1>Statistics</h1>
<Display results = {(good,neutral,bad)}></Display>
<h1>{good}{ neutral} {bad}</h1>
</div>
)
}
const Display = ({good,neutral,bad}) => (
console.log(good),
<table>
<tr>
<td>good</td>
<td>{good}</td>
</tr>
<tr>
<td>neutral</td>
<td>{neutral}</td>
</tr>
<tr>
<td>bad</td>
<td>{bad}</td>
</tr>
<tr>
<td>all</td>
<td>{good+neutral+bad}</td>
</tr>
<tr>
<td>average</td>
<td>{(good-bad)/(good+neutral+bad)}</td>
</tr>
<tr>
<td>positive</td>
<td>{(good)/(good+neutral+bad)}</td>
</tr>
</table>
)
export default App
The way you are passing props is wrong.Try this
<Display good={good} neutral={neutral} bad={bad}></Display>
In Display you're not destructuring the results prop, but the entire props of the component. So either change how you use the component to
<Display {...{good, neutral, bad}} />
which is short for
<Display good={good} bad={bad} neutral={neutral} />
OR
change the Display component to destructure results
const Display = ({results}) => {
const {good, neutral, bad} = results;
return (
// ...
)
}
and how you use it to
<Display results={{good, neutral, bad}} />
you can change to <Display results = {{good:good, neutral:neutral, bad:bad}}>
Lastly you can receive the results prop as below:
const Display = ({results}) => (
console.log(results),
<table>
<tr>
<td>good</td>
<td>{results.good}</td>
</tr>
<tr>
<td>neutral</td>
<td>{results.neutral}</td>
</tr>
<tr>
<td>bad</td>
<td>{resuts.bad}</td>
</tr>
<tr>
<td>all</td>
<td>{good+neutral+bad}</td>
</tr>
<tr>
<td>average</td>
<td>{(results.good-results.bad)/(results.good+neutral+results.bad)}</td>
</tr>
<tr>
<td>positive</td>
<td>{(results.good)/(rsults.good+results.neutral+results.bad)}</td>
</tr>
</table>
)
Note: Remember you can not divide 0, you may probably get an error during division compilation
your problem is here
<Display results = {(good,0,0)}></Display>
it should be
<Display results={{good, neutral:0, bad:0}} />
or probably
<Display results={{good, neutral, bad}} />
I am trying to filter through an array so that when a certain tab is clicked, only those results show. I have managed to isolate the certain variables I want to remain but the others that don't fit the criteria still remain. How do I get the filter method to actually render on the page so that the results can be shown. I have searched for hours for and have tried to get the tbody and sort through that but I just get confused as I am mostly new to javascript and react. Can someone point me in the right direction?
Filter Method
const tbody = document.getElementsByTagName('tbody')
console.log(tbody)
//change active class
function addTabBackground() {
const tabs = document.querySelectorAll('[data-tab]')
window.onload = function () {
tabs.forEach(tab => {
tab.addEventListener('click', () => {
if (tab.getAttribute('data-tab') === 'gains') {
listOfOptions.map(option => {
console.log(option.totalProfit)
})
}
tabs.forEach(tab => {
tab.classList.remove('active')
})
tab.classList.add('active')
})
})
}
}
<div className="outputs" >
<table>
<thead>
<tr>
<th>Date</th>
<th>Stock Name</th>
<th>Price Of Option</th>
<th>Number Of Options</th>
<th>Total Amount Spent</th>
<th>Option Sold At</th>
<th>Amount Of Options Sold</th>
<th>Proft</th>
</tr>
</thead>
{listOfOptions.map(option => {
return (
<tbody>
<tr>
<td>{option.clock}</td>
<td>{option.name.toUpperCase()}</td>
<td>${option.price}</td>
<td>{option.amountOfOptions}</td>
<td>${option.totalAmountSpent.toFixed(2)}</td>
<td>${option.optionPriceSoldAt}</td>
<td>{option.amountOfOptionsSold}</td>
<td style={{ color: option.totalProfit >= 0 ? 'green' : 'red' }}>${option.totalProfit.toFixed(2)}</td>
</tr>
</tbody>
)
})}
</table>
</div>
I have used React-Bootstrap-v5 to get Nav, Nav.Item with eventKey and then passed selectedKey in its onSelect function to change the tabs.
Then once we get the data inside my data variable, I used the map function to go over the array. Inside Map Function I have used the required condition to filter the elements i.e. variable = 'Open' or 'Live'.
This will only show the Open type in Open Tab and Live Type Data inside Live Tab.
Hope it's clear to you.
import React, { useEffect, useState } from 'react';
const TestSeries = () => {
// Declare Variable for data to be fetched from API
const [data, setData] = useState([]);
const fetchTestData = async () => {
const response = await axios.get(site_ip + '/apiLink');
setData(response.data.Content);
};
useEffect(() => {
fetchTestData();
}, []);
// State Variable to keep track of active tab
const [activeTab, setActiveTab] = useState('Open');
return (
<>
<Nav
activeKey={activeTab}
fill
variant="pills"
onSelect={(selectedKey) => {
setActiveTab(selectedKey);
}}
>
<Nav.Item>
<Nav.Link eventKey="Open">Open</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="Live">Live</Nav.Link>
</Nav.Item>
</Nav>
<br />
<Table striped bordered hover>
<thead>
<tr>
<th>#</th>
<th>Column1</th>
<th>Column2</th
<th>Column3</th>
<th>Column4</th>
<th>Data Type</th>
</tr>
</thead>
<tbody>
{data.map((item, index) =>
// Condition by which data will be filtered
item.data_type == activeTab ? (
<tr>
<td>{index + 1}</td>
<td>{item.col1}</td>
<td>{item.col2}</td>
<td>{item.col3} </td>
<td>{item.col4}</td>
<td>{item.data_type}</td>
</tr>
) : null
)}
</tbody>
</Table>
</>
);
};
export default TestSeries;
Result
Assuming from the comment that you have something vaguely looking like:
function Page() {
return (
<>
<Navbar />
<Table />
</>
)
}
What you need to do is to store the current tab in a state, and pass this state down to the Table component so that you can use a Array.filter when rendering your table.
function Page() {
const [activeTab, setActiveTab] = useState(DEFAULT_ACTIVE_TAB)
return (
<>
<Navbar activeTab={activeTab} setActiveTab={setActiveTab} />
<Table activeTab={activeTab} />
</>
)
}
Your Navbar component should have a onClick handler where it is calling the setActiveTab function when the active tab change.
Then in your Table component you should have something like this:
function Table({ activeTab }) {
return (
<table>
...
{listOfOptions
.filter(option => /* something depending on the activeTab */)
.map(option => <... />)
}
</table>
}
Hi I want to have multiple TRs and inside one, have multiple TDs using react I want to loop through my comparedProperties object and create the table in render method dynamically but I get this error:
Uncaught Error: Objects are not valid as a React child (found: object with keys {id, address, long, lat, cityId, cityDistrict, phone,
name, userId, city}). If you meant to render a collection of
children, use an array instead or wrap the object using
createFragment(object) from the React add-ons. Check the render method
of Comparison.
my data object is like this and I can not change its structure:
//this is a samle data, keys in this object can dynamically change elsewhere
let comparedProperties = {
id: [1001,1002],
address: ["abc","def"],
};
this is my code:
class Comparison extends Component {
render() {
let comparedProperties = {
id: [1001, 1002],
address: ["abc", "def"]
};
let comparedItemsData = [];
for (var key in comparedProperties) {
if (comparedProperties.hasOwnProperty(key)) {
let newTR = <tr key={Math.random()} className="compare-table-row">
<td className="table-item-header">
{key}
</td>
{comparedProperties[key].map((item) => {
return <td key={Math.random()} className="table-item">{item}</td>;
})}
</tr>;
comparedItemsData.push(newTR)
}
}
return (
<table className="compare-table">
<tbody>
{comparedItemsData}
</tbody>
</table>
)
}
}
const mapStateToProps = (state) => ({
...state
});
const mapDispatchToProps = (dispatch) => ({
actions: bindActionCreators(Actions, dispatch)
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Comparison);
update answer:
so I figuerd where the problem was but I expexted better error message from react
the problem was that in my comparedProperties I had an object inside the array that caused the error
let comparedProperties = {"id":[101,102],"estateAgency":[{"id":1},{"id":2}]}
Are you trying to do something like that ?
render(){
let comparedProperties = {
id: [1001, 1002],
address: ["abc", "def"],
};
return (
<table>
{Object.keys(comparedProperties).map(key=>(
<tr key={Math.random()} className="compare-table-row">
<td className="table-item-header">
{key}
</td>
{comparedProperties[key].map((item) => (
<td key={Math.random()} className="table-item">{item}</td>
))}
</tr>
))}
</table>
)
}
Or if you want to try as a a stateless comp you insert in your table :
const ComparedItemsData = ({ comparedProperties }) =>(
<React.Fragment>
{Object.keys(comparedProperties).map(key => (
<tr key={Math.random()} className="compare-table-row">
<td className="table-item-header">{key}</td>
{comparedProperties[key].map(item => (
<td key={Math.random()} className="table-item">
{item}
</td>
))}
</tr>
))}
</React.Fragment>
)
const App = ()=>{
let comparedProperties = {
id: [1001, 1002],
address: ["abc", "def"]
};
return (
<table className="compare-table">
<tbody>
<ComparedItemsData comparedProperties={comparedProperties}/>
</tbody>
</table>
)
}
You just need to return the td elements from within map function. Also never specify Math.random() as a key to the react elements because everytime render is called a new key will be assigned and it will force React to re-render the entire component even though nothing would have changed.
for (var key in comparedProperties) {
if (comparedProperties.hasOwnProperty(key)) {
let newTR = <tr key={key} className="compare-table-row">
<td className="table-item-header">
{key}
</td>
//comparedProperties[key] is an array of
// values that I want to make them as td elements
{ comparedProperties[key].map((item) => {
return <td key={item} className="table-item">{item}</td>;
})}
</tr>;
comparedItemsData.push(newTR)
}
}
I am trying to make a table row function like a Link with react-router.
I keep getting the error Cannot read property 'handleClick' of undefined
handleClick(user) {
this.router.transitionTo('index', user);
}
render(){
var userNodes = this.props.posts.map(function(user, i){
return (
<tr onClick={() => this.handleClick(user)}>
<Td>{user.postId}</Td>
<Td>{user.title}</Td>
<Td>{user.body}</Td>
</tr>
)
});
...
Use arrow function as map callback to preserve component context (otherwise this inside the callback will not point to the component instance):
render(){
var userNodes = this.props.posts.map((user, i) => {
return (
<tr onClick={() => this.handleClick(user)}>
<Td>{user.postId}</Td>
<Td>{user.title}</Td>
<Td>{user.body}</Td>
</tr>
)
});
Its a binding issue, you need to bind the context otherwise this will not point to the react component. Either use .bind(this) with function or use arrow function to avoid this kind of problem. Use this:
render(){
var userNodes = this.props.posts.map(function(user, i){
return (
<tr onClick={() => this.handleClick(user)}>
<Td>{user.postId}</Td>
<Td>{user.title}</Td>
<Td>{user.body}</Td>
</tr>
)
}.bind(this));
Or this:
render(){
var userNodes = this.props.posts.map((user, i) => {
return (
<tr onClick={() => this.handleClick(user)}>
<Td>{user.postId}</Td>
<Td>{user.title}</Td>
<Td>{user.body}</Td>
</tr>
)
});
Check the working example:
class App extends React.Component{
handleClick(user){
console.log(user);
}
render(){
var data = [0,1,2,3,4].map((user, i) => {
return (
<tr onClick={() => this.handleClick(user)}>
<td>{user}</td>
<td>{user}</td>
<td>{user}</td>
</tr>
)
});
return(
<table>
<thead>
<th>A</th>
<th>B</th>
<th>C</th>
</thead>
<tbody>
{data}
</tbody>
</table>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<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>
<div id='app'/>
You have to bind it.
var userNodes = this.props.posts.map(function(user, i){}.bind(this));