How can I display one item from this array in the render? - reactjs

I do not know of a way to display these conditions in the render. Nothing is appearing or if I try .map then it gives me errors.
I've tried refactoring, but came up with the same scenario. I've tried assigning the sampleInfo items to a new array but still can't figure out how to display just 1 item from that array in the render.
sampleInfo = [
{
second: 1,
ml: '19ml',
animal: 'Thing1',
weight: '4kg',
capacity: '20ml'
},
{
second: 2,
ml: '38ml',
animal: 'Thing2',
weight: '7kg',
capacity: '35ml'
},
{
second: 3,
ml: '57ml',
animal: 'Thing3',
weight: '12kg',
capacity: '60ml'
}
]
....Some functions here......
onSubmit(onetotwosec, morethantwosec) {
const {time} = this.state;
this.setState((state, props) => ({
scoreArray:state.scoreArray.concat(ms(state.time,{verbose: true})),
time:0,
submit:true
}));
// let myNewanimalArray = state.animalArray.concat({morethantwosec});
// console.log(myNewanimalArray)
let filterAnimal = 0
if((time >= 1000) && (time < 2000)) {
filterAnimal = this.sampleInfo.filter((item) => {
return item.second === 1;
}).map((item) => {
return item.animal
});
console.log(filterAnimal)
}
else if(time > 2000) {
filterAnimal = this.sampleInfo.filter((item) => {
return item.second === 2;
}).map((item) => {
return item.animal
});
console.log(filterAnimal)
}
}
render() {
if((this.state.isOn) === true){
return(
<React.Fragment>
<div>
<h3>Timer:{ms(this.state.time,{verbose: true})}</h3>
</div>
<div>
<button onMouseDown={this.onItemMouseDown} onMouseUp={this.onItemMouseUp}>start</button>
</div>
<div>
<ul>
{this.state.scoreArray.map(function(item,i){
return<li key={i}>{item}</li>
})}
</ul>
</div>
</React.Fragment>
)
} else if ((this.state.isOn) === false){
return(
<React.Fragment>
<div>
<h3>Timer:{ms(this.state.time)}</h3>
</div>
<div>
<button onMouseDown={this.onItemMouseDown} onMouseUp={this.onItemMouseUp}>Start</button><span></span>
<button onClick={this.resetTimer}>Reset</button>
<span></span>
<button onClick={this.onSubmit}>Done!</button>
</div>
<div>
<ul>
{this.state.scoreArray.map(function(item,i){
return<li key={i}>{item}</li>
})}
</ul>
//Here I've tried .map like above but can't figure out what to put!!!!
</div>
</React.Fragment>
I want the Done! button to display the matched animal from the sampleInfo array when the timer reaches a certain time. It should show each item as text such as 'Thing 1' if the timer is between 1 and 2 seconds etc. I can get it to display correctly using the console, but cannot for the life of me think of how to get it into the render. All of the functions are within one class.

I managed it myself (with help from google and all the above - this has taken me 3 days to do such a simple thing), here's the onSubmit function updated:
onSubmit() {
this.setState((state) => ({
scoreArray:state.scoreArray.concat(ms(state.time,{verbose: true})),
time:0,
submit:true
}));
const {time} = this.state;
var filterAnimal
if((time >= 1000) && (time < 2000)) {
filterAnimal = this.sampleInfo.filter((item) => {
return item.second === 1;
}).map((item) => {
return item.animal
}).toString()
this.setState(prevState => ({
animalArray:[filterAnimal,...prevState.animalArray ]})
)
}
And in the render:
{this.state.animalArray}
It works fine. If anyone can see a problem with this do comment.

Related

React Testing Library - Click all buttons returned by function

test("remove list when no entries are present", () => {
render(<ListContainer />);
seedEntries();
expect(screen.getByRole("list")).toBeInTheDocument;
screen.getAllByRole("button", { name: /\-/i }).forEach((entry) => {
userEvent.click(entry)
});
expect(screen.queryByRole("list")).toBeNull();
});
Clicking the button calls the following function:
const handleDeleteEntry = (id) => {
if (entries.length === 10) {
setLimitError(false);
}
let filteredEntries = entries.filter((entry) => entry.id !== id);
setEntries(filteredEntries);
};
and my render method looks like this:
{entries.length > 0 ? (
<ul>
{entries.map((entry, index) => {
return (
<li
key={index}
>
<div>
{entry.name}
</div>
<div>
{entry.address}
</div>
<div>
{entry.phoneNumber}
</div>
<button
type="button"
onClick={() => handleDeleteEntry(entry.id)}
>
-
</button>
</li>
);
})}
</ul>
) : null}
const seedEntries = () => {
for (let i = 0; i < 10; i++) {
userEvent.type(screen.getByTestId("input-name"), "testName" + i);
userEvent.type(screen.getByTestId("input-address"), "testAddress" + i);
userEvent.type(screen.getByTestId("input-phone"), "012345678" + i);
userEvent.click(screen.getByRole("button", { name: /\+/i }));
}
};
Is it possible to have my test run so that each of the deletion buttons are clicked reliably? Currently it appears to be missing every 2nd button (I think due to it being too quick, but I've tried playing around with settimeouts and async awaits to no success) my user-event is on v13.5

How to load virtual dom once the data from api are loaded React

I think that my dom loads faster than data do.
I use useEffect to grab my data and store in localStorage
But when my data are loaded I have to refresh the page one more time to display my fetched data
How can I make my dom wait for the data. I tried to use useState and check whether we get the data from API, I put setLoadDom(true) in my useEffect if statement and then display dom if(loadDom) is true otherwise I show Loading...
From api I get
[
{firstName, lastName, id, completed},
....
{}
]
sortInOrder - get data and transforms it in array where indexes are [a,b,c,d,...] and the value is an array of all objects where lastName === to the letter. tmp['a'] = [{lastName: "Amanda"...},...]
toggleCheckbox - easy to understand, after I toggled checkbox I use localstorage to save my 'checked' status
Functional component
function App() {
const ALPHABET = "abcdefghijklmnopqrstuvwxyz".split("");
const [employeesData, setEmployeesData] =
useState(JSON.parse(localStorage.getItem('data')) ?? []);
const [alphabeticalOrder, setAlphabeticalOrder] = useState([]);
const sortInOrder = () => {
let tmp = []
for(let i = 0; i < ALPHABET.length; i++) {
tmp[ALPHABET[i]] = []
for(let j = 0; j < employeesData.length; j++) {
if(employeesData[j]['lastName'][0].toLowerCase() === ALPHABET[i]) {
tmp[ALPHABET[i]].push(employeesData[j])
}
}
}
for(let i = 0; i < ALPHABET.length; i++) {
if (tmp[ALPHABET[i]].length === 0) {
tmp[ALPHABET[i]] = ['-']
}
}
return tmp
}
const toggleCheckbox = (e, el) => {
let checked = e.target.checked;
setEmployeesData(
employeesData.map(person => {
if(person['id'] === el['id']) {
person['completed'] = checked;
}
return person;
})
)
localStorage.setItem('data', JSON.stringify(employeesData))
setAlphabeticalOrder(sortInOrder())
}
useEffect(() => {
axios.get('example.com')
.then(res => {
let data = JSON.parse(localStorage.getItem('data')) ?? []
if(res.data.length !== data.length) {
localStorage.setItem('data', JSON.stringify(res.data.map(el => ({...el, completed: false}))))
}
})
.catch(e => console.log('Error occured', e))
setAlphabeticalOrder(sortInOrder())
}, [])
return (
<div className="container">
<div className="wrapper">
<div className="employees">
<div className="employees_title align">Employees</div>
<div className="employees_info_block">
{ALPHABET.map((letter, i) => (
<div className="employees_block" key={letter}>
<div className="employee_letter" key={i}>{letter}</div>
{[...alphabeticalOrder[letter] ?? []].map(el => {
if (el[0] != '-') {
return (
<div className="info_field" key={el["id"]}>
<input type="checkbox" checked={el['completed'] ?? false}
onChange={(e) => toggleCheckbox(e, el)}/>
<div className="info_field_text">{el["firstName"]} {el["lastName"]}</div>
</div>
)
} else {
return (
<div>
-
</div>
)
}
})
}
<br/>
</div>
))}
</div>
</div>
<div className="birthday">
<div className="birthday_title align">Employes birthday</div>
</div>
</div>
</div>
);
}
could you try using setEmployeesData after:
localStorage.setItem('data', JSON.stringify(res.data.map(el => ({...el, completed: false}))))

Storing array objects using index on radio button click in react [duplicate]

This question already has answers here:
How to sort an array of integers correctly
(32 answers)
Closed 2 years ago.
How to store radio button values in ascending order of index in an array in react?
let questions;
class App extends Component {
constructor(props) {
super(props);
this.state = {
btnDisabled: true,
questions: [],
};
this.changeRadioHandler = this.changeRadioHandler.bind(this);
this.submitHandler = this.submitHandler.bind(this);
}
changeRadioHandler = (event) => {
const qn = event.target.name;
const id = event.target.value;
let text = this.props.data.matrix.options;
let userAnswer = [];
for (let j = 0; j < text.length; j++) {
userAnswer.push(false);
}
const option = text.map((t, index) => ({
text: t.text,
userAnswer: userAnswer[index],
}));
const elIndex = option.findIndex((element, i) => element.text === id);
let options = [...option];
options[elIndex] = {
...options[elIndex],
userAnswer: true,
};
const question = {
id: event.target.value,
qn,
options,
};
if (this.state.questions.some((question) => question.qn === qn)) {
questions = [
...this.state.questions.filter((question) => question.qn !== qn),
question,
];
} else {
questions = [...this.state.questions, question];
}
console.log(questions);
this.setState({ questions });
if (questions.length === text.length) {
this.setState({
btnDisabled: false,
});
}
};
submitHandler = () => {
console.log("btn clkd", questions);
};
render() {
return (
<div class="matrix-bd">
{this.props.data.header_text && (
<div class="header-qn">
<h5>{this.props.data.header_text} </h5>
</div>
)}
{this.props.data.matrix && (
<div class="grid">
{this.props.data.matrix.option_questions.map((questions, j) => {
return (
<div class="rows" key={j}>
<div class="cell main">{questions.text}</div>
{this.props.data.matrix.options.map((element, i) => {
return (
<div class="cell" key={i}>
<input
type="radio"
id={"rad" + j + i}
name={questions.text}
value={element.text}
onChange={this.changeRadioHandler}
></input>
<label htmlFor={"rad" + j + i}>{element.text}</label>
</div>
);
})}
</div>
);
})}
</div>
)}
<div class="buttonsubmit text-right">
<button
type="button"
id="QstnSubmit"
name="QstnSubmit"
class="btn btn-primary"
disabled={this.state.btnDisabled}
onClick={this.submitHandler}
>
{this.props.labels.LBLSUBMIT}
</button>
</div>
</div>
);
}
}
export default App;
I have added my code where the array is unordered with respect to index, I want to order the array using qn(const qn = event.target.name;) object. Like, how it came from the database, likewise in the same order, it should go into the db.
You can use the sort() method, then have a callback function with two arguments and return them in an accending order, like this:
ans.sort((a, b) =>{return a-b});

How to clear fields array in redux form

I am trying to clear checkbox array in fields array from the redux form. How can I do it?
I'm using redux form FieldArray in the format of members:[filterOption:{}, checkBoxOption:{}]. checkBoxOption value depends o filterOption dropdown. Whenever the user selects an option from filterOption in result they get a list of checkbox from which they have to select from the list of checkBoxOption.
Let's say if a user has selected a value from filterOption and checkBoxOption and now they change the value of filterOption in result they will get a new list of an array for checkBoxOption. The values are getting replaced by the new one but they are not getting uncheck.
I am able to clear checkbox array in values array by using fields.get(event).checkBoxOption = {} but unable to find the solution on how to empty fields array.
Can anyone help me out with this?
<ul className="list-group">
{
fields.map((member, index) => (
<li className="list-group filter-select-box" key={index}>
<SelectBox
name={`${member}.filterOption`}
label="Metadata Attribute"
options={attributes.map(attribute => ({
value: attribute.name,
label: attribute.name,
disabled: selectedAttributes.filter(value => value.name === attribute.name).length > 0,
}))}
isChange
handleSelectChange={opt => handleOptionChange(index, opt.value)}
/>
{checkStatus(index) && (
<div className="select-checkbox-option form-group">
{
getCheckboxList(index).map((checkboxItem, x) => (
<CheckBox
key={x}
type="checkbox"
name={`${member}.checkBoxOption.${checkboxItem}`}
label={checkboxItem}
value={`${member}.checkBoxOption.${checkboxItem}`}
id={checkboxItem}
/>
))
}
</div>
)}
</li>
))
}
<li className="list-group filter-select-box">
<button className="add-filter" type="button" onClick={() => fields.push({})}>
<img src={filterPlus} alt="" className="filterPlus" />
Add Filter
</button>
{touched && error && <span>{error}</span>}
</li>
</ul>
the function which is getting checkbox value
const handleOptionChange = (event, nameAttribute) => {
const value = {
index: event,
status: true,
name: nameAttribute,
};
let selectedAttributesStatus = false;
for (let i = 0; i < selectedAttributes.length; i += 1) {
if (value.index === selectedAttributes[i].index) {
selectedAttributes[i].name = value.name;
selectedAttributesStatus = true;
}
}
if (!selectedAttributes.length || !selectedAttributesStatus) {
setSelectedAttributes([...selectedAttributes, value]);
}
setShowOptions([...showOption, value]);
getCategoricalVar(extractorId, nameAttribute)
.then((resp) => {
const newAttributeValue = {
index: event,
value: resp,
};
fields.get(event).checkBoxOption = {};
setSelectedIndex(false);
console.log('fields.get(event).checkBoxOption: ', fields.get(event).checkBoxOption);
let attributeValuesStatus = false;
for (let i = 0; i < attributeValues.length; i += 1) {
if (newAttributeValue.index === attributeValues[i].index) {
attributeValues[i].value = newAttributeValue.value;
attributeValuesStatus = true;
}
}
if (!attributeValues.length || !attributeValuesStatus) {
setAttributeValues([...attributeValues, newAttributeValue]);
}
})
.catch(printError);
};
Function which is setting the value on checkbox
const getCheckboxList = (index) => {
for (let i = 0; i < attributeValues.length; i += 1) {
if (attributeValues[i].index === index) {
return attributeValues[i].value;
}
}
return [];
};

Multiple dropdown filters is not working together in react js

In my react app, I have 3 dropdown menus, the first selects a few properties with ascending and descending options, the second filter by hair color and the third filter by professions. However, depending on which dropdown i use first, the other 2 won't work afterwards unless i refresh the browser. The data is fetched from somewhere using axios and the dropdown menus are child components and props passed back as callback functions.
I think I need to reset the state for the other search criteria, have tried but that didn't work. I have also look up for solutions for multiple filters to work, but I don't seem to be able to apply to my problems.
Here is my initial state of component:
class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
imageIsLoaded: false,
orderBy: "",
order: "",
profession: "",
hairColor: "",
};
this.doOrderBy = this.doOrderBy.bind(this);
this.doOrder = this.doOrder.bind(this);
this.handleColor = this.handleColor.bind(this);
this.handlePro = this.handlePro.bind(this);
}
Here are the handlers
doOrderBy(e) {
const newOrderBy = e.target.value;
this.setState({
orderBy: newOrderBy,
});
}
doOrder(e) {
const newOrder = e.target.getAttribute("data-value");
this.setState({ order: newOrder });
}
doOrderBy is an array ["name", "age", weight"..]
doOrder is ["asc", "desc"]
handleColor(e) {
const uniqueHairColor = [
...new Set(this.state.data.map(item => item.hair_color)),
];
const newColor = [...e.target.selectedOptions].map(opt => opt.value);
const newValue = newColor == " " ? uniqueHairColor : newColor;
this.setState({
hairColor: newValue,
});
}
handlePro(e) {
const newProfession = [...e.target.selectedOptions].map(opt =>
opt.value);
const allProfessions = this.state.data
.map(item => item.professions)
.flat(1);
const uniqueProfession = [...new Set(allProfessions)].sort();
const newValue = newProfession == " " ? uniqueProfession :
newProfession;
this.setState({
profession: newValue,
});
}
Here is the sorting logic:
render() {
const {data, imageIsLoaded, hairColor, orderBy, order, profession}
=this.state;
let sorted = data;
if (order) {
if (orderBy !== ("number of friends" && "number of professions"))
{ sorted = _.orderBy(sorted, item => {
return item[orderBy]}, order);
} else if (orderBy === "number of friends") {
sorted = _.orderBy(sorted, item => {
return item.friends.length},order);
} else {sorted = _.orderBy(sorted, item => {
return item.professions.length}, order);
}
} else if (hairColor) {
sorted = _.filter(sorted, item => _.includes(hairColor,
item.hair_color));
} else if (profession) {
sorted = _.filter(sorted, { professions: profession });
} else {
sorted = data;
}
here are the dropdown menus as components in the render:
<OrderMenu
doOrder={this.doOrder}
order={order}
orderBy={orderBy}
doOrderBy={this.doOrderBy}
placeholder="Select by..."
/>
<HairMenu
data={data}
handleHairInput={this.handleColor}
placeholder="Choose hair color..."
/>
<ProfessionMenu
data={data}
handleInputPro={this.handlePro}
placeholder="Choose profession..."
/>
and the data is rendered as
<AnimalCards sorted={sorted}/>
Ideally, I would like to have the filters working together, doing multiple filtering at the same time. But at the moment, when i use the first filter, the second or third won't work at all unless I refresh the browser. Any help to point out what and where I am missing would be great. Thanks.
import React, { Component, Fragment } from "react";
class AnimalCards extends Component {
render() {
const { sorted } = this.props;
const Jobless = <div>No profession to show</div>;
const NoFriends = <div>No friends to show</div>;
return (
<Fragment>
<div className="flip-card-container">
{sorted.map(animal => {
return (
<div className="flip-card" key={animal.id}>
<div className="flip-card-inner">
{/* ---------------START front ----------- */}
<div className="flip-card-front">
<div
className="flip-card-front-top"
style={{ backgroundColor: animal.hair_color }}
/>
<div className="img-container">
<img
className="img-circle"
src={animal.thumbnail}
alt={`${animal.name}`}
style={{ border: "5px solid white" }}
/>
</div>
<div className="flip-card-front-bottom">
<div className="name">{animal.name}</div>
<div className="attributes">
<div className="age-group">
<img
className="ageIcon"
src={require("../images/axe.png")}
alt="ageIcon"
/>
<div className="age">{animal.age + " "}
yrs</div>
</div>
<div className="weight-group">
<img
className="weightIcon"
src={require("../images/weight.png")}
alt="weightIcon"
/>
<div className="weight"> {animal.weight} kg
</div>
</div>
<div className="height-group">
<img
className="ageIcon"
src={require("../images/height.png")}
alt="heightIcon"
/>
<div className="height">{animal.height} cm
</div>
</div>
</div>
<div className="professions">
Number of professions:
<div className="professions-num">
{animal.professions.length === 0
? Jobless
: animal.professions.length}
</div>
</div>
<div className="friends">Number of friends:</div>
<div className="friends-num">
{animal.friends.length === 0
? NoFriends
: animal.friends.length}
</div>
</div>
</div>
{/* ------------START front ----------- */}
{/* ------------START back ------------- */}
<div className="flip-card-back">
<div className="back-container">
<div className="back-header">Professions</div>
<div className="back-list">
{animal.professions.length === 0
? Jobless
: animal.professions.join(", ")}
</div>
</div>
<div className="back-container">
<div className="back-header">Friends</div>
<div className="back-list">
{animal.friends.length === 0
? NoFriends
: animal.friends.join(", ")}
</div>
</div>
</div>
{/* -------------END back ------------- */}
</div>
</div>
);
})}
</div>
</Fragment>
);
}
}
export default AnimalCards;
Here is the data (in json) that i fetched:
fetchAnimals() {
const apiUrl =
"https://raw.githubusercontent.com/rrafols/
mobile_test/master/data.json";
axios.get(apiUrl).then(({ data }) => {
localStorage.setItem("data", data);
this.setState({
data: data.Brastlewark,
imageIsLoaded: true,
});
});
}
componentDidMount() {
this.fetchAnimals();
}
The problem was actually trivial. You had used an if-else-if block!
if (order) { //If this is true the other else ifs will not be executed
if (orderBy !== ("number of friends" && "number of professions"))
{ sorted = _.orderBy(sorted, item => {
return item[orderBy]}, order);
} else if (orderBy === "number of friends") {
sorted = _.orderBy(sorted, item => {
return item.friends.length},order);
} else {sorted = _.orderBy(sorted, item => {
return item.professions.length}, order);
}
} else if (hairColor) { // If the above condition was met this will not be checked
sorted = _.filter(sorted, item => _.includes(hairColor,
item.hair_color));
} else if (profession) { //If any of the above was true this would not be checked
sorted = _.filter(sorted, { professions: profession });
} else {
sorted = data;
}
So... turn the if-else-if blocks to individual/independant if blocks!
Also, small advice on the algorithm too, always do your filtering before the sorting!
Here is the fixed logic.
render() {
const {
data,
imageIsLoaded,
hairColor,
orderBy,
order,
profession
} = this.state;
let sorted = data;
if (hairColor) {
sorted = _.filter(sorted, item => _.includes(hairColor,
item.hair_color));
}
if (profession) {
sorted = _.filter(sorted, {
professions: profession
});
}
if (order) {
if (orderBy !== ("number of friends" && "number of professions")) {
sorted = _.orderBy(sorted, item => {
return item[orderBy]
}, order);
} else if (orderBy === "number of friends") {
sorted = _.orderBy(sorted, item => {
return item.friends.length
}, order);
} else {
sorted = _.orderBy(sorted, item => {
return item.professions.length
}, order);
}
}
}

Resources