display icon based on file type - reactjs

I want to display icons based on the file type. From the object that I am fetching from strapi I can get the file extension. But I want to render it dynamically on the table.
const TableData = () => {
const [data, setData] = useState([]);
const getData = () => {
axios.get("http://localhost:1337/document-uploads").then((response) => {
console.log(response);
const myData = response.data;
setData(myData);
});
};
useEffect(() => getData(), []);
return (
<div className="my-5">
<Table striped bordered hover>
<thead>
<tr>
<th>Icon</th>
<th>File Name</th>
<th>Description</th>
<th>Author</th>
<th>Date created</th>
</tr>
</thead>
<tbody>
{data &&
data.map((file: File) => (
<tr key={file.id}>
<td>
{() => {
if (file.document.ext == ".pdf") {
return <img src={PDF} />;
} else if (file.document.ext == ".xml") {
return <XML />;
} else {
return <JPEG />;
}
}}
</td>
<td>{file.document.name}</td>
<td>{file.description}</td>
<td>{file.author}</td>
<td>{file.created_at}</td>
</tr>
))}
</tbody>
</Table>
</div>
);
};
export default TableData;
I am getting the error: "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."

You can try the code without the anonymous function inside, just start from the if statement without everything wrapped inside a function.
{
if (file.document.ext == ".pdf") {
return <img src={PDF} />;
} else if (file.document.ext == ".xml") {
return <XML />;
} else {
return <JPEG />;
}
}
You can as well try different approaches with more modern javascript and ternary operators:
{
file.document.ext == ".pdf" ? <img src={PDF} /> :
file.document.ext == ".xml" ? <XML /> : <JPEG />
}
Which is the same as the first code block.

Related

How to Access Variable Outside of Return in a Child from Parent?

If I keep my buttons in my child, this works great except for the fact I have buttons showing in every row. I'd like to move my buttons to the parent so that they just display once at the bottom of the screen. I'v tried moving my buttons to the return on the parent and putting my state code above the return. I think this would work except that my "count" value in my child, for my graphql variable "offset", now needs access to the "count" in my parent. Not sure how to make this happen. I'm pretty new to react and graphql.
import React, { Component, useState } from 'react';
import { useQuery, gql } from '#apollo/client';
import {Table, Spinner, Button} from 'react-bootstrap'
const Customer_List = gql`
query getCust ($configID: Int, $first: Int, $offset: Int ) {
docsconfig (configID:$configID first:$first offset:$offset) {
SubDomain
ConfigID
CustID
customers {
FirstName
LastName
}
}
}
`
function CustomerList() {
const { loading, error, data} = useQuery(Customer_List, {
variables: {
configID: 1436,
first: 10,
offset: count
},
}
);
if (loading) return <td> <Spinner animation="border" role="status">
<span className="sr-only">Loading...</span>
</Spinner> </td>
if (error) return <p>Error :(</p>;
return data.docsconfig.map(({ CustID, SubDomain, customers, FirstName, LastName}) => (
<tr key={CustID}>
<td>{customers.FirstName}</td>
<td>{customers.LastName}</td>
<td>{SubDomain}</td>
</tr>
)
)
}
function Customers () {
const [count, setCount] = useState(0)
function increase() {
setCount(count + 10);
}
function decrease() {
setCount(count - 10);
if (count === 0) {
setCount(count + 0);
}
}
return (
<Table striped bordered hover>
<thead >
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>SubDomain</th>
</tr>
</thead>
<tbody>
<CustomerList />
</tbody>
<tr>
<button onClick={decrease}>-</button>
<button onClick={increase}>+</button>
</tr>
</Table>
);
}
export default Customers;
Pass count as a prop to CustomerList.
CustomerList
function CustomerList({ count }) { // <-- desctructure count from props
const { loading, error, data} = useQuery(
Customer_List,
{
variables: {
configID: 1436,
first: 10,
offset: count // <-- pass to config/options
},
}
);
...
Customers
function Customers () {
const [count, setCount] = useState(0)
...
return (
<Table striped bordered hover>
<thead >
...
</thead>
<tbody>
<CustomerList count={count} /> // <-- pass count to CustomerList
</tbody>
<tr>
<button onClick={decrease}>-</button>
<button onClick={increase}>+</button>
</tr>
</Table>
);
}

How to display button opposite of level in React?

How to display button opposite of its status. Suppose its showing open then the button should appear Withdraw and if the status is appearing Withdraw button should appear Open in Reactjs.
I have a Json object which carries the necessary information.
Here is code is what I have had tried..
const initial = {
description: [
{
name: 'Alex',
level: 'open',
},
{
name: 'Sycus',
level: 'open',
},
{
name: 'Rasku',
level: 'Withdraw',
}
]
}
export default function App() {
const [state, setState] = React.useState(initial)
const [show , setshow] = React.useState(true)
const handleClick = () => {
return
}
const butFunction = () => {
return state.description.map(sub => {
const { level } = sub
return (
if(level === 'open'){
<button>Withdraw</button>
}
else{
<button>Open</buton>
}
)
})
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div>
<table>
<tbody>
{
state.description.map(desc => (
<tr>
<td>
{desc.name}
</td>
<td>
{desc.level}
</td>
<td>
{ show && butFunction()}
</td>
</tr>
))
}
</tbody>
</table>
</div>
</div>
);
}
Instead of rendering two different buttons for two conditions you can use ternary operator to conditionally change the text of single button like this:
return(
<button>{level===‘open’?’withdraw’:’open’}</button>
)
I don't think you need the map inside the butFunction. That would print 3 buttons for every entry. Instead, pass the function a parameter telling it which level it is.
Example below:
export default function App() {
const [state, setState] = React.useState(initial)
const [show , setshow] = React.useState(true)
// Use the level passed in
const butFunction = (level) => {
if(level === 'open'){
return <button>Withdraw</button>;
} else {
return <button>Open</button>;
}
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div>
<table>
<tbody>
{state.description.map(desc => (
<tr>
<td>
{desc.name}
</td>
<td>
{desc.level}
</td>
<td>
{show && butFunction(desc.level)} // <- Pass it here
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
Not it will only return one button per entry.

Conditional statement within react render meth

I am new to react and I am trying to add a condition within the render method so that if the value framework1 is empty I need not add a text within the HTML.
Below is my code.
buildResults = () => {
const {
results
} = this.state;
return {
<div className={`${block}__results`}>
<table>
<thead></thead>
<tbody>
{ results.map(item => this.buildResult(item)) }
</tbody>
</table>
</div>
}
buildResult = (data) => {
const {
framework1,
framework2
} = data;
return (
<tr className={`${block}__row`}>
<td className={`${block}__cell ${block}__cell--ticker`}>
/* I need to display the text "view" only if framework1 is not empty */
View
</td>
</tr>
)
}
Use an if statement to check whether the there is data, in this case, I am checking whether the data in framework1 is a string.
buildResults = () => {
const {
results
} = this.state;
return {
<div className={`${block}__results`}>
<table>
<thead></thead>
<tbody>
{ results.map(item => this.buildResult(item)) }
</tbody>
</table>
</div>
}
buildResult = (data) => {
const {
framework1,
framework2
} = data;
return (
<tr className={`${block}__row`}>
<td className={`${block}__cell ${block}__cell--ticker`}>
if (framework1.indexOf("ST1") ){
// display something else
}
else {
View
}
</td>
</tr>
)
}

Why I get error: "Maximum update depth exceeded" when trying to sort table?

I am trying to sort table but I get an error when trying to use function in render. This is my code snippet:
/*index.jsx*/
import React, {Component, useState } from 'react';
//Functional Component
const Row = ({id, spec, qNumber,i}) => (
<tr>
<th key={i} id={id} scope={"row"}>{i}</th>
<td key={spec} id={id}>{spec}</td>
<td key={qNumber} id={id}>{qNumber}</td>
</tr>
);
class LightBoardPage extends Component{
constructor(props){
super(props);
this.state = {
list: [],
error: null,
loaded: false
}
this.compareBy.bind(this);
this.sortBy.bind(this);
}
buildList =(data)=>{
console.log(data);
this.setState({list: data})
}
componentDidMount(){
console.log('did mount')
let url = './data.json';
fetch(url)
.then(response => response.json())
.then(this.buildList)
.catch(error => {
this.setState({error});
})
}
compareBy(key) {
return function (a, b) {
if (a[key] < b[key]) return -1;
if (a[key] > b[key]) return 1;
return 0;
};
}
sortBy(key) {
let arrayCopy = [...this.state.list];
arrayCopy.sort(this.compareBy(key));
this.setState({list: arrayCopy});
}
render(){
const rows = this.state.list.map( (rowData) => <Row {...rowData}/>);
console.log('render');
let o=0;
return (
<div >
{this.sortBy('spec')}
<table className="table table-hover">
<thead>
<tr>
<th>#</th>
<th>Specialistas</th>
<th>Eilės numeris</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
{this.state.error &&
<h3>{this.state.error}</h3>
}
</div>
)
}
}
export default LightBoardPage;
If I use sortBy() with "onClick" it works but otherwise I get this error:
Error
I am new in react and I don't know what I can do to make sorting work. So what I could do to make this error dissapera?
P.S. This is my code: Code
I wrote () => this.sortBy('spec') and everything fixed

React 'this' Context within Method

I am trying to call a Method on a Button click like this (the button is returned within a method itself):
<button onClick={removeClick}>Remove</button>
this is the method i am trying to call
removeClick = event => {
console.log('clicked');
};
im always getting errors like '_this2 is undefined'
i have already tried binding the method in the constructor.
an arrow function within the onClick didn't work as well
removeClick = event => {
console.log('clicked');
};
renderWeather(cityData) {
const name = cityData.city.name;
const temps = cityData.list.map(weather => weather.main.temp - 273.15);
const pressures = cityData.list.map(weather => weather.main.pressure);
const humidities = cityData.list.map(weather => weather.main.humidity);
const { lon, lat } = cityData.city.coord;
return (
<tr key={lon + lat}>
<td>
<GoogleMap lon={lon} lat={lat} />
<span className="city-name">{name}</span>
</td>
<td>
<WeatherChart data={temps} color="red" unit="°C" />
</td>
<td>
<WeatherChart data={pressures} color="orange" unit="hPA" />
</td>
<td>
<WeatherChart data={humidities} color="blue" unit="%" />
</td>
<td>
<button onClick={this.removeClick}>Remove</button>
</td>
</tr>
);
}
render() {
return (
<table className="table table-hover">
<thead>
<tr>
<th>City</th>
<th>Temperature (°C) </th>
<th>Pressure (hPA)</th>
<th>Humidity (%)</th>
<th />
</tr>
</thead>
<tbody>{this.props.weather.map(this.renderWeather)}</tbody>
</table>
);
}
}
const mapStateToProps = ({ weather }) => ({
weather
});
const mapDispatchToProps = {
removeCity
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(WeatherList);
this is how the my component looks like
I hope anyone can help me
try
renderWeather = cityData => {
...
}
and because you are using your clickhandler for a set of data you may want to pass some kind of identifier to it
removeClick = id => event => {
console.log(`Removing data for ${id}`)
}
and call it by
<button onClick={() => {this.removeClick(id)}}>Remove</button>
Try to pass this as a second param to map
<tbody>{this.props.weather.map(this.renderWeather, this)}</tbody>
class Item extends React.Component {
removeClick = event => {
console.log('clicked');
}
renderWeather(cityData) {
return <button onClick={this.removeClick}>Remove</button>
}
render() {
return (
<table className="table table-hover">
<thead>
<tr>
<th>City</th>
<th>Temperature (°C) </th>
<th>Pressure (hPA)</th>
<th>Humidity (%)</th>
<th />
</tr>
</thead>
<tbody>{this.props.weather.map(this.renderWeather, this)}</tbody>
</table>
)
}
}
const weather = [
{
city: {name: '123'}
}
];
ReactDOM.render(
<Item weather={weather} />,
document.getElementById('root')
)
<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='root'></div>

Resources