React get value from key:value array - reactjs

Beginner question. I know this is a simple question but I haven't been able to get this to work. I'm passing an object which holds an array of k:v pairs to a component. Eventually this props will contain multiple k:v pairs, but for now I'm just passing the one.
[{goal: 20000}]
In the component I'm trying to grab the value, 20000, so I can display it on screen. I can't seem to get just the number. If I look at props.goal I get the entire k:v.
[{goal: 20000}]
If I try props[0].goal I get 'TypeError: undefined is not an object (evaluating 'props[0].goal')'
What am I missing? Thanks for any help.
Update:
Here is the entire code for the component in question.
import { React, useState } from "react";
import Form from "react-bootstrap/Form";
import { Row, Col, Button } from "react-bootstrap";
import "./../css/Goal.css";
const Goal = (props) => {
// const [goal, setGoal] = useState("");
const [record, setRecord] = useState("");
const monthlyGoal = 2;
console.log("props[0]");
console.log(props[0]); //undefined
console.log("props");
console.log({ props }); //See below
props: Object
goal: Object
goals: [{goal: 20000}] (1)
const handleInput = (event) => {
console.log(event);
event.preventDefault();
setRecord(event.target.value);
console.log(record);
};
const defaultOptions = {
significantDigits: 2,
thousandsSeparator: ",",
decimalSeparator: ".",
symbol: "$",
};
const formattedMonthlyGoal = (value, options) => {
if (typeof value !== "number") value = 0.0;
options = { ...defaultOptions, ...options };
value = value.toFixed(options.significantDigits);
const [currency, decimal] = value.split(".");
return `${options.symbol} ${currency.replace(
/\B(?=(\d{3})+(?!\d))/g,
options.thousandsSeparator
)}${options.decimalSeparator}${decimal}`;
};
return (
<Form>
<Row className="align-items-center flex">
<Col sm={3} className="goal sm={3}">
<Form.Control
id="inlineFormInputGoal"
placeholder="Goal"
// onChange={(e) => setGoal(e.target.value)}
/>
<Button type="submit" className="submit btn-3" onSubmit={handleInput}>
Submit
</Button>
</Col>
<Col>
<h1 className="text-box">
Goal: {formattedMonthlyGoal(monthlyGoal)}
</h1>
</Col>
</Row>
</Form>
);
};
export default Goal;
Update 2:Here is the parent component:
import React, { useEffect, useState } from "react";
import Goal from "./Goal";
import axios from "axios";
const Dashboard = () => {
const [dashboardinfo, setdashboardinfo] = useState([]);
useEffect(() => {
async function fetchData() {
try {
const data = (await axios.get("/api/goals/getgoals")).data;
setdashboardinfo(data);
} catch (error) {
console.log(error);
}
}
fetchData();
}, []);
return (
<React.Fragment>
<Goal dashboardinfo={dashboardinfo} />
</React.Fragment>
);
};
export default Dashboard;

If you get an object like the following from console logging destructured props:
{
dashboardinfo: {goals: [{goal: 20000}]}
}
You need to use props.dashboardinfo.goals[0].goal to get the value.

Your props contains the object "dashboardinfo" so you need to do
props.dashboardinfo.goals[0].goal
or a better way is to destructure your props object like this
const Goal = ({dashboardinfo: { goals }}) => {
...
goals[0].goal
...
}

I believe I've resolved my issue. It wasn't so much a problem with accessing the key:value as I thought, because when the page was initialized I was able to grab the value and display it fine. However, when I refreshed the page I lost all of the props data and that resulted in an error. I tracked it down to the useState didn't seem to be updating the value before I was trying to read it. So I added a useEffect in the child component.
const Goal = (props) => {
const [goal, setgoal] = useState([]);
useEffect(() => {
setgoal(props.goal);
console.log("the goal", goal);
}, [props.goal, goal]);
...
This seems to have worked as I'm getting the information I want and not getting any errors when I refresh. This may not be the ideal way to go about this but it is working.

Related

next.js trouble with cookies

I need to save my data from array to cookies, but I have some issues. :)
At the begining here bellow my code:
import { LineChartProps } from "./LineChart.props";
import { Input } from "../Input/Input";
import { Button } from "../Button/Button";
import cookie from "js-cookie";
import React, { useState, useEffect } from "react";
export const LineChart = ({ }: LineChartProps): JSX.Element => {
const [inputValue, setInputValue] = useState("");
const [citiesList, setCitiesList] = useState<string[]>(
(cookie.get("citiesList")?.split(",")) || [
"London",
"Kyiv",
"Los Angeles"
]
);//array in state
const onChange = ((event) => {
setInputValue(event.target.value);
});
const onClick = ((event) => {
setCitiesList((currentArray) => [...currentArray, inputValue])
console.log(citiesList)
});
useEffect(() => {
cookie.set("citiesList", JSON.stringify(citiesList), { path: '' });
}, [citiesList]);
return (
<div>
<Button appearance="primary" onClick={onClick}>click me</Button>
<Input type="text" name="search" onChange={onChange} />
<div>
<p>{inputValue}</p>
</div>
</div>
)
};
To simplify it I'll also add here screenshot of my code:
As you see I have an array with cities. I want to save my array data to cookies, because I use next.js, but not a react.
I have button and input field:
So when I start my app, my array looks like this:
but for example if I reaload the page, I'll see this:
Then if I reload one more time, this:
So, what is better? Prevent cookies.set if all of elements are already in cookies, or there is other (better) solution for this?
And the second problem is: When I add some cities to my cookies Array, at the begining it looks like this:
But if I refresh the page twice, I'll see this:
Do you have any idea?
And if so, thanks in advance! Will be waiting for any solution:)
Try to use JSON.parse instead:
const [citiesList, setCitiesList] = useState<string[]>([]);
useEffect(() => {
const defaultCities = ["London","Kyiv","Los Angeles"]
const cities = cookie.get("citiesList");
if (!cities) {
setCitiesList(defaultCities)
} else {
try {
setCitiesList(JSON.parse(cities))
} catch (err) {
setCitiesList(defaultCities)
}
}
}, [])

how to execute on click function to show filtered results from search bar in react

I am working on a component where the user searches a term and it is returned to them through a filter. I am using useContext hook to pass data from db via axios. I would like to use the button in the CompSearch component to render the results rather than having them show up on a keystroke. My question is how do I render the results via button click?
Here is the code
Follow these steps to achieve that.
Change the input element into an uncontrolled component.
Get the value using reference in React.
import React, { useContext, useRef, useState } from "react";
import CompanyInfoList from "./CompanyInfoList";
import { CompListContext } from "./CompListContext";
const CompSerach = () => {
const [company, setCompany] = useContext(CompListContext);
const [searchField, setSearchField] = useState("");
const [searchShow, setSearchShow] = useState(false);
const filtered = company.filter((res) => {
return res.company.toLowerCase().includes(searchField.toLowerCase());
});
const inputRef = useRef(null); // 1. Create the ref
const handleClick = () => {
const val = inputRef.current.value; // 3. Get the value
setSearchField(val);
if (val === "") {
setSearchShow(false);
} else {
setSearchShow(true);
}
};
const searchList = () => {
if (searchShow) {
return <CompanyInfoList filtered={filtered} />;
}
};
return (
<>
<div>
<em>
NOTE: if you search "ABC" or "EFGH" you will see results - my goal is
to see those results after clicking button
</em>
<br />
<input
type="search"
placeholder="search Company Title"
ref={inputRef} {/* 2. Assign the ref to the Input */}
/>
<button onClick={handleClick}>Enter</button>
</div>
{searchList()}
</>
);
};
export default CompSerach;
https://codesandbox.io/s/show-on-button-click-68157003-rot6o?file=/src/TestTwo/CompSearch.js
Let me know if you need further support.
const handleChange = (e) => {
setSearchField(e.target.value);
if (e.target.value === "") {
setSearchShow(false);
} else {
setSearchShow(true);
}
**setCompany(e.target.value);**
};
i think your question is similar with autocomplete.

Error - Rendering multiple instances in react search component

I am building a search component where the user types in the value and it filters through some dummy data and renders the result for the user to see. The problem is when I type in one character in the search field i get the entire array of data on every character I type. So for example there are 4 data strings in the array so if I type in two characters in the search bar then my result is 8.
Here is the code and the problem duplicated: Any help is appreciated.
The answer is below, basically, the code was loading in an extra map from CompanyInfoList component. I also had a useContext that was not needed in the same component, so i removed it and replaced results.map from useContext setState hook to filtered.map. filtered was the prop that needed to be passed down from CompSearch to CompanyInfoList. The last change I made was to delete the RenderList component and remove return RenderList in the serachList component to CompanyInfoList
CompSearch.js
import React, {useContext, useState} from "react";
import CompanyInfoList from "./CompanyInfoList";
import { CompListContext } from "./CompListContext";
const CompSerach = () => {
// const [input, setInput] = useState('');
const [results, setResults] = useContext(CompListContext);
const [searchField, setSearchField] = useState("");
const [searchShow, setSearchShow] = useState(false);
const filtered = results.filter((res) => {
return (
res.name.toLowerCase().includes(searchField.toLowerCase()) ||
res.employee.toLowerCase().includes(searchField.toLowerCase()) ||
res.date.toLowerCase().includes(searchField.toLowerCase()) ||
res.amount.toLowerCase().includes(searchField.toLowerCase())
);
});
const handleChange = (e) => {
setSearchField(e.target.value);
if (e.target.value === "") {
setSearchShow(false);
} else {
setSearchShow(true);
}
};
function searchList() {
if (searchShow) {
return <CompanyInfoList filtered={filtered} />;
}
}
return (
<>
<div>
<input
type="search"
placeholder="search Company Title"
// input="input"
// value={input}
onChange={handleChange}
// onChange={handleChange}
/>
</div>
{searchList()}
</>
);
};
export default CompSerach;
CompanyInfoList.js
import Results from "./Results";
const CompanyInfoList = ({ filtered }) => {
const fltr = filtered.map((result) => (
<Results
key={result.id}
name={result.name}
employee={result.employee}
date={result.date}
amount={result.amount}
/>
));
return <div>{fltr}</div>;
};
export default CompanyInfoList;

The React useState() hooks stores undefined always even the data that is to be stored logs correctly using console.log();

Here is the where I am having the problem,
const handleCLick = () => {
const parsedId = getYouTubeID(videoLink);
console.log(parsedId);
setVideoId(parsedId);
console.log(videoId);
}
Here when I am trying to log the 'parsedId' it logs the data correctly
ioNng23DkIM
And after using the setVideoId() function when I try to log the value it returns undefined
undefined
Here is a snap shot of the log output.
Home.js code:
import React, { useRef, useState } from "react";
import { Link } from "react-router-dom";
import getYouTubeID from 'get-youtube-id';
function Home(props) {
const [videoLink, setVideoLink] = useState();
const [isBool, setBool] = useState(false);
const [videoId, setVideoId] = useState();
const urlRef = useRef();
const handleChange = (event) => {
setVideoLink(event.target.value);
if (urlRef.current.value === '') {
alert('Please enter a URL');
setBool(true);
} else {
setBool(false);
}
}
const handleCLick = () => {
const parsedId = getYouTubeID(videoLink);
console.log(parsedId);
setVideoId(parsedId);
console.log(videoId);
}
return (
<section className="homeLayout">
<div className="logo-display">
<img className="logo-img" alt="logo" src="./logo.png" />
<h1>WatchIt</h1>
</div>
<div className="searchlayer">
<form>
<input ref={urlRef} id="videoLink" placeholder="Enter the youtube video URL:" onBlur={handleChange} required />
<Link style={{ pointerEvents: isBool ? 'none' : 'initial' }} to={`/play?=${videoId}`} onClick={handleCLick}>Play</Link>
</form>
</div>
</section>
);
}
export default Home;
You can use useEffect to solve your problem.
Use effect will listen to you state change n then you can perform logic in there.
The problem you're facing is because setState will set the value eventually, not immediately (Usually this means the update will be visible when the component is rendered again). If you want to do something after the value is set, you need to use useEffect.
Splitting your handleClick we get,
const handleCLick = () => {
const parsedId = getYouTubeID(videoLink);
console.log(parsedId);
setVideoId(parsedId); // Queue the change for `videoId`
}
useEffect(() => {
console.log(videoId);
}, [videoId]); // Call this function when the value of `videoId` changes

React hooks, does not change the checked property to checkbox

I am trying to change the ckeckbox property, every time I write a new value to the map, in my case when I click on the checkbox, it only changes the value of the checked property, for example, the initial value is true, on all subsequent clicks it will be false, false, false ... What's wrong here?
import React,{ useState,useEffect } from "react";
import {useSelector} from 'react-redux'
const ChangeArticle = (props) => {
const prevArticle = props.changeArtcle;
const age_groups = useSelector(state => state.app.age_groups);
const [checkedAges, setCheckAges] = useState(new Map());
const handleChangeCheckBoxe = (event) => {
setCheckAges(checkedAges => checkedAges.set(event.target.value, event.target.checked));
console.log("checkedItems: ", checkedAges);
}
useEffect(() => {
if(prevArticle.ages){
prevArticle.ages.forEach((age) =>{
setCheckAges(checkedAges => checkedAges.set(age.toString(), true));
});
}
},[prevArticle]);
return (<div>
{age_groups.map(age => {
return (<div key={age.id}>
<input type="checkbox" checked={checkedAges.has(age.id.toString()) ? checkedAges.get(age.id.toString()) : false} value={age.id} onChange={handleChangeCheckBoxe}
/>
{ age.title }
</div>)
}) }
</div>);
}
export default ChangeArticle;
In the handleChangeCheckBoxe function, you are only changing the values withing the Map. React only does a shallow reference check to see if the Map had changed. Since the reference is the same, the it will not re-render the component as you would expect.
You can change the function to be similar to the following to create a new Map and assign it to state.
const handleChangeCheckBoxe = (event) => {
setCheckAges(checkedAges => new Map(checkedAges.set(event.target.value, event.target.checked)));
console.log("checkedItems: ", checkedAges);
}
You can see this working in the following code sandbox https://codesandbox.io/s/wispy-leftpad-9sji0?fontsize=14&hidenavigation=1&theme=dark

Resources