Here is my code:
import { Col, Container, Row } from "react-bootstrap";
import { useTrafficSnapShot } from "./useTrafficSnapShot";
export default function TrafficSnapShot() {
let [error, isLoading,
selectedSnapShotList,
searchSnapShot] = useTrafficSnapShot();
return (
<div>
{
!isLoading &&
<Container fluid>
<Row><Col>Traffic Snap Shot</Col></Row>
<Row>
<Col><input onChange={e => searchSnapShot(e.target.value)} required type="text" /></Col>
</Row>
{
selectedSnapShotList.map((snapShort, index) => {
let temp = [], obj;
obj = <Col><div>{snapShort.location}</div></Col>
if ((index % 4) === 0) {
temp.push(<Row>);
}
temp.push(obj);
if ((index % 3)===0 ){
temp.push(</Row>)
}
return temp;
})
}
</Container>
}
</div>
)
}
I want to group 4 Col in a Row.
I got the following error message on temp.push(</Row>).
Line 26:43: Parsing error: Unexpected token (26:43)
How can I fix the problem?
JSX element converts to React.createElement
Doing like this: temp.push(<Row>); and this temp.push(</Row>) really makes no sence.
<Row>test</Row> converts into React.createElement(Row, null, 'test') and it returns an object
How you expect to separate that object in 2 halves ?!
Just move the Row outside of the .map method
<Row>
{ selectedSnapShotList.map((snapShort, index) => {
return <Col key={index}><div>{snapShort.location}</div></Col>
})}
</Row>
Related
I was making a search bar component that modifies an array and then a mapping function that displays the resulted array it as the page results, the problem is that the page is delaying to update, in other words when I type a character in the search bar nothing changes but when I add another character the results are being updated with the first character input only and the.
I was using a hook state to hold the value of the search input and then using a filter function to update the array, finally I used a mapping function to display the modified array data as card components. As I said the problem is the delay that the website takes to update the array and it seams that the problem is with the state hook I uses but I couldn't solve that problem.
I also reuse the filtered array to display search suggetions
Here is app.js
import React, { useState } from "react";
import ReactDOM from "react-dom/client";
import Card from "./components/Card";
import resourcesData from "./resourcesData";
import { type } from "#testing-library/user-event/dist/type";
function App() {
const [filteredList, setFilteredList] = useState(resourcesData);
const [searchTerm, setSearchTerm] = useState("");
const changeInput = (event) => {
setSearchTerm(event);
};
function handleSearchTerm(event) {
setSearchTerm(event.target.value);
var updatedList = [...resourcesData];
updatedList = updatedList.filter((val) => {
if (searchTerm === "") return val;
else if (
val.title.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
) {
return val;
} else if (
val.thematicArea
.toLocaleLowerCase()
.includes(searchTerm.toLocaleLowerCase())
) {
return val;
}
});
setFilteredList(updatedList);
}
return (
<div className="App">
<input
type="text"
value={searchTerm}
onChange={handleSearchTerm}
className="input"
></input>
<div className="dropdown">
{filteredList.slice(0, 10).map((item) => (
<div
onClick={() => changeInput(item.title)}
className="dropdown-row"
key={item.title}
>
{item.title}
</div>
))}
</div>
<div className="cards">
{filteredList.map((value, index) => (
<Card
resourceURL={value.link}
thumbnailURL=""
title={value.title}
subtitle=""
date={value.date}
description=""
cost=""
cardkeywords={
value.cost === "free"
? [
value.level,
value.language,
value.type,
value.thematicArea,
value.cost,
]
: [
value.level,
value.language,
value.type,
...value.thematicArea.split(","),
]
}
key={index}
/>
))}
</div>
</div>
);
}
export default App;
In the function handleSearchTerm you use setSearchTerm(event.target.value); and after you are using searchTerm which updates asynchronously.
Use in this function event.target.value.
function handleSearchTerm(event) {
const newValue = event.target.value;
setSearchTerm(newValue);
var updatedList = [...resourcesData];
updatedList = updatedList.filter((val) => {
if (newValue === "") return val;
else if (
val.title.toLocaleLowerCase().includes(newValue.toLocaleLowerCase())
) {
return val;
} else if (
val.thematicArea
.toLocaleLowerCase()
.includes(newValue.toLocaleLowerCase())
) {
return val;
}
});
setFilteredList(updatedList);
}
From the API I'm getting an array of 40 objects. I want to show only 5 items. So my idea is to show only every 4th item, and skip the others. My idea is that I will filter the array first and if the condition is met, it will return the data, maybe using map?
const Weather = () => {
const [key, setKey] = useState([]);
const [data, setData] = useState({
city: "",
country: ""
})
const API = '28876c50c36221de5f008fa752cb3f1a';
const dataWeather = async () => {
await axios.get(`https://api.openweathermap.org/data/2.5/forecast?q=${data.city},${data.city}&appid=${API}`)
.then(res => {
console.log(res.data)
// const {resData} = res.list;
const { list: resData } = res.data
console.log(resData);
setKey(resData);
})
}
const handleClick = (e) => {
const { name, value } = e.target;
setData(prev => {
return {
...prev,
[name]: value
}
})
}
const trigger = (e) => {
e.preventDefault();
dataWeather()
}
return (
<Form>
<Container>
<Form>
<Form.Row>
<Col>
<Form.Control placeholder="City" onChange={handleClick} name="city" value={data.city} />
</Col>
<Col>
<Form.Control placeholder="Country" onChange={handleClick} name="country" value={data.country} />
</Col>
<Button variant="primary" type="submit" onClick={trigger}>
Submit
</Button>
</Form.Row>
</Form>
<div className="grid">
{
key !== null && (
key.map(dataMap =>
if (dataMap.dt_txt % 4 === 0) {
<Card data={dataMap.weather[0].description} date={dataMap.dt_txt} imgSrc={dataMap.weather[0].icon} temp={Math.floor(dataMap.main.temp - 273.15)} />
}
)
)
}
</div>
</Container>
</Form>
)
}
Use the % remainder operator to keep every Nth item, and skip the others.
list.filter((item, index) => index % 4 === 0)
map() will always return an array of the same length that it is called on, so removing elements is not possible without mutating it directly which is not the appropriate use of the method.
That being said, React will accept children with values of false , null, undefined, and true but won't render them. So instead of explicitly filtering you can simply return null for elements you don't want to render.
key &&
key.map((dataMap, index) => {
if (index % 4 === 0) {
return <Card data={dataMap.weather[0].description} date={dataMap.dt_txt} imgSrc={dataMap.weather[0].icon} temp={Math.floor(dataMap.main.temp - 273.15)} />
} else {
return null;
}
}
Alternatively you can apply a filter call before mapping. filter() returns an array of elements that return true for the passed condition. You can then chain your map() call on returned array.
key &&
key
.filter((_, index) => index % 4 === 0)
.map(dataMap => (
<Card data={dataMap.weather[0].description} date={dataMap.dt_txt} imgSrc={dataMap.weather[0].icon} temp={Math.floor(dataMap.main.temp - 273.15)} />
)
The contests array has a length of two, and I should see two divs with the "test" text in them, however nothing is displaying. I can log to the console before returning in the map, and it logs twice as expected yet nothing is returned. Any ideas?
return (
<Row>
<Col>
<h3>Open Contests</h3>
{contests.map((contest, i)=>{
return (
<div key={i}>
<p>test</p>
</div>
)
})}
</Col>
</Row>
)
Edit: here is the whole component
import React, {useState, useEffect} from 'react'
import {Row, Col} from 'react-bootstrap';
const OpenContests = () => {
const [contestRows, setContestRows] = useState([]);
const [contests, setContests] = useState([]);
//fetch open contests on initial render
useEffect(()=>{
const fetchOpenContests = async () => {
const response = await fetch('/contests/open');
const data = await response.json();
setContestRows(data)
}
fetchOpenContests();
},[])
const checkRow = (row) => {
let arr = contests;
let index = contests.findIndex(contest=> contest.contestID === row.intContestID);
if(index > -1){
let league = {
leagueID: row.intLeagueID,
leagueName: row.strLeagueName
}
arr[index].contestLeagues = [...arr[index].contestLeagues, league];
setContests(arr);
}else{
arr.push({
contestID: row.intContestID,
contestType: row.strContestType,
contestLeagues: [
{
leagueID: row.intLeagueID,
leagueName: row.strLeagueName
}
],
entry: row.decEntryFee,
bankroll: row.decInitialBankRoll,
prizepool: row.decPrizePool,
start: row.dtmStart,
end: row.dtmEnd,
minPlayers: null,
maxPlayers: null
})
}
}
//run when contest rows array changes
useEffect(()=>{
if(contestRows.length > 0){
contestRows.forEach(row=> {
checkRow(row)
})
}
},[contestRows])
return (
<Row>
<Col>
<h3>Open Contests</h3>
{contests.map((contest, i)=>{
return (
<div key={i}>
<p>test</p>
</div>
)
})}
</Col>
</Row>
)
}
export default OpenContests
Here is the contests array
I have a function which gets a data from an API using axios. I've created a custom axios functional component to call which only work inside a function (it throws an Invalid hook call error when used on class).
I've searched several questions on this site but all seems to be using a class. So, is it possible to populate a second dropdown based on the selection?
PS. I'm using react-bootstrap. I tried to implement it but the second dropdown does not update.
Here is my code: (Please bear with the code. It's dirty )
import React, { useState, useEffect } from 'react';
import { Container, Row, Col, Button, Form } from 'react-bootstrap';
import Loader from '../Components/Loader';
import Err404 from '../Views/Err404';
import { GetRequest } from '../Hooks/GetRequest';
function Profile() {
let divs = GetRequest('/divisions')
let subs = GetRequest('/subjects')
const [selectedDiv, setSelectedDiv] = useState("")
const [selectedSub, setSelectedSub] = useState("")
let content = null
let subOptions = null
let divOptions = null
let subArr = null
let subFilter = null
let disabled = true
if (divs.error || subs.error) {
return (<Err404 />)
}
if (divs.loading || subs.loading) {
content = <Loader />
}
if (divs.data && subs.data) {
content = null
divOptions =
divs.data.map((div) => (
<option key={div._id}>{div.name}</option>
))
subArr =
subs.data.map((sub) => (
{
key: sub._id,
courseCode: sub.courseCode,
division: sub.division
}
))
subOptions =
subArr.map((sub) => (
<option key={sub.key}>{sub.courseCode}</option>
))
}
const handleDiv = (e) => {
disabled = false
setSelectedDiv(e.target.value);
console.log("selDiv: ", selectedDiv)
console.log("subArr: ", subArr)
subFilter =
subArr.filter((sub) => (
sub.division === selectedDiv
))
subOptions =
subFilter.map((sub) => (
<option key={sub.key}>{sub.courseCode}</option>
))
console.log("subOps: ", subOptions, "\nsubfil: ", subFilter)
}
const handleSub = (e) => {
console.log("selSub: ", e.target.value);
setSelectedSub(e.target.value);
}
return (
<Container fluid="sm" className="p-2">
{content ? content :
<Form>
<Row>
<Col>
<Form.Group controlId="exampleForm.ControlSelect1">
<Form.Label>Divison</Form.Label>
<Form.Control
as="select"
onChange={(e) => handleDiv(e)}
defaultValue="Choose ..."
>
<option disabled>Choose ...</option>
{divOptions}
</Form.Control>
</Form.Group>
</Col>
<Col>
<Form.Group controlId="exampleForm.ControlSelect1">
<Form.Label>Subject</Form.Label>
<Form.Control
as="select"
onChange={(e) => handleSub}
defaultValue="Choose ..."
disabled={disabled}
>
<option disabled>Choose ...</option>
{subOptions}
</Form.Control>
</Form.Group>
</Col>
</Row>
</Form>
}
</Container>
)
}
export default Profile
Managed to make it work
const handleDiv = (e) => {
disabled = false
setSelectedDiv(e.target.value);
}
if (subArr) {
console.log("selDiv: ", selectedDiv)
console.log("subArr: ", subArr)
subFilter =
subArr.filter((sub) => (
sub.division === selectedDiv
))
subOptions =
subFilter.map((sub) => (
<option key={sub.key}>{sub.courseCode}</option>
))
console.log("subOps: ", subOptions, "\nsubfil: ", subFilter)
}
I have been attempting to render words with most frequencies. I have done with fetching API.To render words with there total count.I also have setState words and mapped words array in render().I was expected words with there counts. I only get numbers as 1 1 1 1 1 1 1 1 1 1 1 2 1. in table data.
import React, { Component } from "react";
import { Grid, Row, Col, Table } from "react-bootstrap";
import axios from "axios";
class About extends Component {
state = {
counts: [],
posts: [],
words: []
};
componentDidMount() {
axios({
url:
"https://cors-anywhere.herokuapp.com/http://terriblytinytales.com/test.txt",
responseType: "text"
})
.then(res => {
const posts = res.data;
const newPosts = posts.split(/[0-9]+\./).map(post => post.split("?"));
// console.log(newPosts);
this.setState({
posts: newPosts
});
return res;
})
.then(res => {
const texts = res.data;
let words = texts.replace(/[.]/g, "").split(/\s/);
let freqMap = [];
words.map(w => {
if (!freqMap[w]) {
freqMap[w] = 0;
}
freqMap[w] += 1;
console.table(freqMap);
return freqMap;
});
this.setState({
words: freqMap
});
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<Grid>
<Row>
<Col xs={12} sm={6} md={6}>
<h1>fetched data</h1>
<ol>
{this.state.posts.map((post, i) => (
<li key={i} style={{ listStyle: "none" }}>
{post.map((p, j) => (
<p key={j}>{p + (j % 2 === 0 ? "?" : "")}</p>
))}
</li>
))}
</ol>
</Col>
<Col xs={12} sm={6} md={6}>
<Row>
<Table striped bordered condensed hover>
<tbody>
<tr>
{this.state.words.map((post, i) => <td key={i}>{post}</td>)}
</tr>
</tbody>
</Table>
</Row>
</Col>
</Row>
</Grid>
);
}
}
export default About;
The problem you are having is due to your implementation of Arrays with your freqMap variable:
.then(res => {
const texts = res.data;
let words = texts.replace(/[.]/g, "").split(/\s/);
let freqMap = []; // this should NOT be an array
words.map(w => {
if (!freqMap[w]) {
freqMap[w] = 0;
}
freqMap[w] += 1;
console.table(freqMap);
return freqMap;
});
this.setState({
words: freqMap
});
})
Arrays in javascript are not linked lists of key value pairs although javascript will not complain when you try something like let freqMap["Word"] = 1 like you are doing in your code. this will lead to different problems especially when trying to loop over your array's contents, just like the problem you are having.
Arrays cannot use strings as element indexes (as in an associative
array) but must use integers. Setting or accessing via non-integers
using bracket notation (or dot notation) will not set or retrieve an
element from the array list itself, but will set or access a variable
associated with that array's object property collection.
You should be using an object instead:
.then(res => {
const texts = res.data;
let words = texts.replace(/[.]/g, "").split(/\s/);
let freqMap = {}; // this should be an object
words.map(w => {
if (!freqMap[w]) {
freqMap[w] = 0;
}
freqMap[w] += 1;
console.table(freqMap);
return freqMap;
});
this.setState({
words: freqMap
});
})
and then in the JSX loop over the object.keys which is an array of the object keys:
{Object.keys(this.state.words).map((post, i) => (
<tr key={i}>
<td>{post}</td>
<td>{this.state.words[post]}</td>
</tr>
))}