I have a search page, which queries an api to get an array of objects. It then renders a for each element of the array. I'm trying to achieve this like so:
export default class SearchScreen extends Component {
constructor(props) {
super(props);
this.state = {
results: null
};
}
componentDidMount() {
const apiUrl =
"foo";
fetch(apiUrl)
.then(response => response.json())
.then(response =>
this.setState({
results: response.results
})
);
}
render() {
{this.state.results ? (
const items = this.state.results.map((item, index) => {
return (
<div>
<SearchResult name={item.name} />
</div>
);
})
return <div>{items}</div>;
) : (<div> LOADING...</div>)}
}
So, if this.state.results is not null, it should map its contents to a const, and then generate SearchResult elements.
It is complaining that const is an unexpected keyword. Is there something wrong with conditionally defining constants?
The syntax is incorrect, below is fixed one:
render() {
const items = this.state.results ? (
this.state.results.map((item, index) => {
return (
// Use better key
<div key={index}><SearchResult name={item.name} /></div>
);
}
)) : 'LOADING...';
return <div>{items}</div>;
}
Statements (like const items = [];) cannot be used with the ternary operator. You can only use expressions. If you want to write a full statement, you have to use if.
You can change the logic a bit to accomplish what you want with the ternary operator though:
return this.state.results ? (
<div>
{this.state.results.map((item, index) => (
<div>
<SearchResult name={item.name} />
</div>
))}
</div>
) :
(
<div> LOADING...</div>
);
Try to fix like this:
render() {
return (
<div>
{this.state.results
? this.state.results.map((item, index) => <SearchResult name={item.name} />)
: 'LOADING...'}
</div>
);
}
Related
I try to fetch object vorlagen with ID 6310 from this API and want to show if property vorlageAngenommen is true or false.
The JSON object looks as follows:
My code that does not run, looks as follows (I am not sure if this is a good base at all):
import useSWR from "swr";
import "./styles.css";
const fetcher = (...args) => fetch(...args).then((res) => res.json());
const arrayFindObjectByProp = (arr, prop, val) => {
return arr.find((obj) => obj[prop] == val);
};
export default function App() {
const { data, error } = useSWR(
"https://www.bfs.admin.ch/bfsstatic/dam/assets/14856203/master",
fetcher
);
const specificVoting = null;
console.log("swr error: ", error);
console.log("swr data: ", data);
return (
<div className="App">
<div style={{backgroundColor: "red"}}>
{data ? (
{specificVoting = arrayFindObjectByProp(data.schweiz.vorlagen, vorlagenId, '6310')}
<h4>{specificVoting.vorlageAngenommen}</h4>
) : (
<h1>loading...</h1>
)}
</div>
</div>
);
}
I created a sandbox as well that can be found here.
Any idea how I can fetch and present a specific element of an array where I know the ID?
Many thanks for your feedback in advance.
Your code has a few issues.
First: You should extract the value outside of the return statement
Second; You should pass on the property value as a string to arrayFindObjectByProp function
Lastly: Since the value returned is a boolean, you need to convert it into a string to display in the component
export default function App() {
const {data, error } = useSWR(
"https://www.bfs.admin.ch/bfsstatic/dam/assets/14856203/master",
fetcher
);
console.log("swr error: ", error);
console.log("swr data: ", data);
const specificVoting =
data && arrayFindObjectByProp(data.schweiz.vorlagen, "vorlagenId", "6310");
return (
<div className="App">
<div style={{ backgroundColor: "red" }}>
{data ? (
<h4>{specificVoting.vorlageAngenommen.toString()}</h4>
) : (
<h1>loading...</h1>
)}
</div>
</div>
);
}
working demo
Try this:
const fetcher = (...args) => fetch(...args).then((res) => res.json());
export default function App() {
const { data } = useSWR(
"https://www.bfs.admin.ch/bfsstatic/dam/assets/14856203/master",
fetcher
);
const item = data?.shchweiz?.vorlagen.find(
(item) => item.vorlagenId === 6310
);
return (
<div className="App">
<div style={{ backgroundColor: "red" }}>
{data ? (
<>
vorlageAngenommen value:{" "}
{item?.vorlageAngenommen ? `true` : `false`}
</>
) : (
<h1>loading...</h1>
)}
</div>
</div>
);
}
https://codesandbox.io/s/react-array-find-object-by-prop-forked-z8lg1?file=/src/App.js:50-673
it seems that
{specificVoting = arrayFindObjectByProp(data.schweiz.vorlagen, vorlagenId, '6310')}
should be replaced by followed
{specificVoting = arrayFindObjectByProp(data.schweiz.vorlagen, 'vorlagenId', '6310')}
i think its this line
{specificVoting = arrayFindObjectByProp(data.schweiz.vorlagen, vorlagenId, '6310')}
It should have been this way:
{specificVoting = arrayFindObjectByProp(data.schweiz.vorlagen, "vorlagenId", '6310')}
"vorlagenId" should be passed as string and not variable.
so the problem is I have a search functionality everything works, except that when an item has not been found, you see it should display the text "champion has not been found" but it is not. I would appreciate the help Where am I making a mistake?
import data from './data.json'
import './Champions.css'
import Skills from './Skills'
import CloseIcon from '#material-ui/icons/Close';
const Champions = ({searchValue}) => {
const [toggleShow, setToggleShow] = useState(false);
const [currentSelectedChampion, setCurrentSelectedChampion] = useState({});
const handleSelectChampion = (id) => {
if (!toggleShow) setToggleShow(true);
const currentChampion = data.filter((champ) => champ.id === id)[0];
setCurrentSelectedChampion(currentChampion);
};
function filterChampions(champion) {
return champion.name.toLowerCase().includes(searchValue.toLowerCase());
}
{data.filter(filterChampions).length === 0 && (<div className='not__found'>
<h1>No champion has been found</h1>
</div>)}
return (
<div className="champions">
{data.filter(filterChampions).map((champion) => {
return (
<div key={champion.id} onClick={() => handleSelectChampion(champion.id) } >
<div className="champion">
<img className="champion__Image" src={champion.image}></img>
<h4 className="champion__Name">{champion.name}</h4>
{toggleShow && currentSelectedChampion.id === champion.id && (
<>
<Skills currentChampion={currentSelectedChampion} />
<CloseIcon onClick={() => setToggleShow(false)}/>
</>
)}
</div>
</div>
);
})}
</div>
);
};
export default Champions
The map in line {data.filter(filterChampions).map((champion) => { will not return anything for empty array.
Consider the following examples.
[].map(e => 'called'); // []
[3].map(e => 'called'); // ['called']
So if {data.filter(filterChampions) returns an empty array the map will return empty array and not the div with class not__found.
What you need to do is something as following
const showChamtions = () => {
// Put the filtered data in a variable
const selectedChampions = champions.filter((element) => element.score > 12);
// If data is there do what you intend to do with it else not_found div
if (selectedChampions && selectedChampions.length > 0) {
return selectedChampions.map((element) => <p>{element.name}</p>);
} else {
return (
<div className="not__found">
<h1>No champion has been found</h1>
</div>
);
}
};
Example - https://codesandbox.io/s/map-on-empty-array-i6m1l?file=/src/App.js:349-741
You can modify your code similar to this using a conditional operator as well.
{data.filter(filterChampions).map((champion) => {
if(data.filter || champion){
return (
<div className='not__found'>
<h1>No champion has been found</h1>
</div>
)
}
This if statement is not nesserasy, if an item has not been found => data.filter(filterChampions) will be an empty array, the map function will return nothing, the if statement doesn't even run.
It you want to display the message, you could simply use this:
{data.filter(filterChampions).length === 0 && (<div className='not__found'>
<h1>No champion has been found</h1>
</div>)}
I have the following:
const OrderItems = (props) => {
const { Order } = props.res
return (
<div>
{Order.map(order => {
if (order.OrderLines.OrderLine instanceof Array) {
order.OrderLines.OrderLine.map(line => (
<OrderLineItem data={line} />
))
}
})}
</div>
)
}
export default OrderItems
And the component:
const OrderLineItem = ({data}) => {
console.log(data)
return (
<div>Order Line Item</div>
)
}
Nothing is rendered and nothing is logged to the console.
However if I do:
const OrderItems = (props) => {
const { Order } = props.res
return (
<div>
{Order.map(order => {
if (order.OrderLines.OrderLine instanceof Array) {
order.OrderLines.OrderLine.map(line => console.log(line))
}
})}
</div>
)
}
The data I want is logged to the console. How can I render or pass the data from line please?
You don't return from .map() that's the reason. You can use && instead of if.
Try as the following instead:
{Order.map(order =>
order.OrderLines.OrderLine instanceof Array &&
order.OrderLines.OrderLine.map(line => <OrderLineItem data={line} />)
)}
While getting the props in the child component
const OrderLineItem = ({ data }) => {
console.log(data)
// DO WHAT YOU WANT TO DO WITH THE DATA!
return (
<div>Order Line Item</div>
)
}
In my Class component App.jsx I have the following function:
import { Position } from "./Position";
...
getPositionData = val => {
const { players } = this.props;
var res = players.filter(o=>Object.values(o).includes(val))
return res
};
Which is being handled at render() when I click on a subcomponent inside a <div>, like so:
<div className="field-row">
{this.getPlayersByPosition(players, 4).map((player,i) => (
<Position key={i} handleClick={this.getPositionData}>{player.name}</Position>
))}
</div>
On its turn, Position.jsx handles onClick():
export const Position = props => (
<div
className={`position-wrapper ${
isManager(props.children) ? null : "field"
}`}
onClick={() => props.handleClick(props.children)}
>
{props.children}
</div>
);
But now I'd like to handle a second function when <div> is clicked, say:
toggleFullScreen() {
this.setState({ fullScreen: !this.state.fullScreen });
}
which I would pass, on its own, like so:
onClick={this.toggleFullScreen.bind(this)}
How do I handle BOTH functions onClick() at once in
<div className="field-row">
...
</div>
?
Just like you can invoke multiple functions at once in a JS listener with
element.addEventListener('click', () => {
fn1();
fn2();
});
You can do the same thing in React by defining the onClick to be a function which invokes both:
<Position
key={i}
getPositionData={this.getPositionData}
toggleFullScreen={toggleFullScreen}
>{player.name}</Position>
export const Position = props => (
<div
className={`position-wrapper ${
isManager(props.children) ? null : "field"
}`}
onClick={() => {
props.getPositionData(props.children);
props.toggleFullScreen();
}}
>
{props.children}
</div>
);
I'm using sortable drag and drop which works fine. The problem is that I'd like users to be able to remove items. The SortableItem component isn't accessible as it came with the code, so I can't pass an event handler that takes index as an argument. Here's what I have so far:
const SortableItem = SortableElement(
({value}) =>
<ul>{value}</ul>
);
const SortableList = SortableContainer(({items}) => {
return (
<ul>
{items.map((value, index) => (
<SortableItem key={`item-${index}`} index={index} value={value} />
))}
</ul>
);
});
export class BlocksContainer extends React.Component {
constructor(props){
super(props);
this.state = {
items: [],
};
}
onSortEnd = ({oldIndex, newIndex}) => {
this.setState({
items: arrayMove(this.state.items, oldIndex, newIndex),
});
};
addBlock = (block) =>{
let arr = [...this.state.items, block];
this.setState({items: arr})
}
removeBlock = (index) => {
let remove = [...this.state.items];
remove.filter(block => block === index);
this.setState({items:remove})
}
render() {
return (<div><div onChange={console.log(this.state)} className="sortableContainer"><SortableList items={this.state.items} onSortEnd={this.onSortEnd} /></div>
<h2>Blocks</h2>
<button onClick={() => this.addBlock(<BannerImage remove={this.removeBlock} />)}>Banner Image</button>
<button onClick={() => this.addBlock(<ShortTextCentred remove={this.removeBlock}/>)}>Short Text Centred</button>
<h2>Layouts</h2>
<hello />
</div>
)
}
}
Since you dont have control over the events of the SortableItem component, you can wrap that component in a component you do have control over.
For example, if i wanted to add a click handler to the SortableItem, i would add it instead to the div wrapper:
const SortableList = SortableContainer(({ items }) => {
return (
<ul>
{items.map((value, index) => (
<div onClick={this.someEventHandler}>
<SortableItem key={`item-${index}`} index={index} value={value} />
</div>
))}
</ul>
);
});