Render number of elements based on users input - javascript

I am trying to render multiple Input elements based on number that user enters. I store the value in my count constant that dynamically changes. Then in my returnForm function I am trying to return the elements using for loop, but with no luck.
const returnForm = () =>
{
let items = [];
for (var i = 0; i < count; i++)
{
items.push(
<div key={i}>
<TextInput source="name" validate={required()} />
<br></br>
<ArrayInput source="tags" label="resources.vid.fields.tags" style={{ width: '40%' }}>
<SimpleFormIterator>
<TagsEdit addLabel={true} label="resources.vid.fields.tag" />
</SimpleFormIterator>
</ArrayInput>
<br></br>
<TagsList addLabel={true} label="resources.vid.fields.tagsList" />
</div>
);
return items;
}
}
Elements are only rendered one time even though value of count is different. Any ideas what am I doing wrong?
Thank you in advance.

try to move return out of loop

Related

React: Managing dozens of button states in a function component

This is my first React project. I am using Bootstrap grid to make a "+" shaped cluster of buttons that go from 1 to 31, which means I cannot use map to reduce the amount of lines of code I have.
const ButtonPlusGrid = () => {
const [colorButtonID, setColorButtonID] = useState(0);
const handleColorChange = () => {
if (colorButtonID === 3) {
setColorButton1(1);
} else {
setColorButton1(colorButtonID + 1);
}
};
let buttonClass;
if (colorButtonID === 0) {
buttonClass = 'btn-secondary col-2';
// btn-secondary for gray color.
} else if (colorButtonID === 1) {
buttonClass = 'btn-success col-2';
// btn-succes for green.
} else if (colorButtonID === 2) {
buttonClass = 'btn-danger col-2';
// btn-danger for red.
} else if (colorButtonID === 3) {
buttonClass = 'btn-primary col-2';
// btn-primary for blue.
}
// the empty divs are to correct the spacing for each row.
return (
<>
<div className="row">
<div className="col-1" />
<div className="col-1" />
<div className="col-1" />
<button
type="button"
className={buttonClass}
id="num1"
onClick={handleColorChange}
>
1
</button>
<div className="col-1" />
<div className="col-1" />
<div className="col-1" />
</div>
<div className="row">
<div className="col-1" />
<div className="col-1" />
<div className="col-1" />
<button
type="button"
className="btn-secondary col-1"
id="num2"
>
2
</button>
<button
type="button"
className="btn-secondary col-1"
id="num3"
>
3
</button>
<div className="col-1" />
<div className="col-1" />
<div className="col-1" />
</div>
...
</>
);
};
I am trying to change the color of each button on click (by changing their class names) but doing it this way will be difficult, replicating the same logic 31 times is too much code, and I can't think of any elegant way to handle it.
I created the button grid using Bootstrap grid to make it follow a specific shape.
For each button, I assigned a specific ID (num1 => num31) because I am planning to save each state in a database and when I open the webpage again, each button keeps the same colors.
These are just 2 rows from the whole grid. I just wrote the code to change the color of one button based on the state.
My question is:
How do I handle all those 31 buttons? Is there a more efficient way to manage each state? or should I overhaul this code because it's repetitive?
I thought of creating a React class component but I don't have an idea how it can be designed for each button, but I'll let you guys decide what's the best approach here.
PS: here is how the buttons look like: Here is the link.
You can manage the state for all the buttons in 1 single state variable. I've created an example to demonstate
import React from "react";
// Just an example to demonstate the functionality, you can changes this to return the correct css class instead.
const getStyle = (number) => {
switch (number) {
case 3:
return "pink";
case 2:
return "green";
case 1:
return "purple";
default:
return "papaywhip";
}
};
export default function App() {
const [colorIdMapping, setColorIdMapping] = React.useState({});
const handleClick = (index) => {
setColorIdMapping({
...colorIdMapping, // Remember all the other values
[index]: ((colorIdMapping[index] || 0) + 1) % 4 // Set current index to number 0, 1, 2 or 3
});
};
return (
<div className="App">
{/* Demo of rendering 10 buttons, render them any way you like. */}
{Array.apply(0, Array(10)).map(function (x, i) {
return (
<div
style={{ background: getStyle(colorIdMapping[i]) }}
onClick={() => handleClick(i)}
>
button
</div>
);
})}
</div>
);
}
You could also create a button component that keeps track of its own state. But in this case I would manage all the state in one place like the example above, so you can easily store and retrieve it (from the db).
But as an example, it could look like this:
const MyButton = () => {
const [number, setNumber] = React.useState(0);
const handleClick = () => {
setNumber((number + 1) % 4);
};
return (
<div onClick={handleClick} style={{ background: getStyle(number) }}>
button
</div>
);
};
Working example: https://codesandbox.io/s/loving-feistel-jw8vci

how to create an array of arrays in react?

I'm trying to code a wordle clone using React, but the twist is that the player can choose the number of attempts and the length of the word. And i want to rendre the box depending on these two parameter so i used the following line :
var grid = Array(this.props.nbrAttempts).fill(0).map(row => new Array(this.props.wordLenght).fill(' '))
the first time the component render i get a 4*4 array, but after changing the parameter i get always a 1*1, and i can't figure what's the problem.
the Box component:
function App() {
const [boxParam, setBoxParam] = useState({nbrAttempts: 4, wordLenght : 4,});
let renderBox = ()=>{
setBoxParam({
nbrAttempts: document.getElementById('nbAttempts').value,
wordLenght : document.getElementById('wordLength').value,
})
}
return (
<div className="App">
{/* add input field with number of attempts as label */}
<label htmlFor="nbAttempts">Attempts</label>
<input type="number" id="nbAttempts"/>
<label htmlFor="nbAttempts">Word length</label>
<input type="number" id="wordLength"/>
<button onClick={()=>renderBox()}>OK</button>
<Box nbrAttempts={boxParam.nbrAttempts} wordLenght={boxParam.wordLenght} />
</div>
);
}
I just replaced
var grid = Array(this.props.nbrAttempts).fill(0).map(row => new Array(this.props.wordLenght).fill(' '))
with
let grid = [];
for (let i = 0; i < this.props.nbrAttempts; i++) {
grid.push(new Array(this.props.wordLenght).fill('_'));
}
and everything just worked fine

React .map() won't update when .filter changes

I have a page with a search, you can filter the results with a range slider. I have an onChange event on the slider that should dynamically set a .filter param. I am passing that param to a chained .sort().filter(props.filter).map().
When I use the slider, it is changing the parameter that I'm passing through, but the .map() is not updating the results.
Any help greatly appreciated:
let genderSpecificFilter = 'All';
let rangeSpecificFilter = 'All'
let sendFilteredDoctors = () => filterDoctors(genderSpecificFilter,rangeSpecificFilter);
function changeRangeFilterParams() {
rangeSpecificFilter = checkBoxDistanceMap[document.getElementById('range-miles').value];
sendFilteredDoctors = filterDoctors(genderSpecificFilter,rangeSpecificFilter);
console.log(rangeSpecificFilter)
}
<PrintSearchResults
doctors={doctors}
filters={sendFilteredDoctors}
/>
import PrintDoctor from "./printdoctor";
function PrintSearchResults(props) {
return (
<div>
{props.filters}
<p className="text-xl text-gray-350">
Total Results:
<span className="ml-3" id="total-results-num">
{props.doctors.results
.sort((a, b) => a.locations[0].distance - b.locations[0].distance)
.filter(props.filters).length}
</span>
</p>
{props.doctors.results
.sort((a, b) => a.locations[0].distance - b.locations[0].distance)
.filter(props.filters)
.map((doctor, key) => (
<PrintDoctor
image={doctor.image}
url={doctor.url}
fullName={doctor.fullName}
specialties={doctor.specialties}
locations={doctor.locations}
gender={doctor.gender}
key={key}
/>
))}
</div>
);
}
export default PrintSearchResults
Edit:
On change coming from here:
<input onChange={changeRangeFilterParams} type="range" min="1" max="6" className="slider" defaultValue="6" id="range-miles" />

react hooks: input onChange first character can not be deleted

I got some inputs to create/update a formular,
1) if value={question}, everything works except that I can not delete the last character (= first character of the input)
2) if I dont mention value, it's all good except when I want to change questions orders with Chevron icon button, database is well changed but input value is still displayed at the last place.
<input
type="text"
value={question}
placeholder="blabla"
onChange={event => {
event.preventDefault();
const value = event.target.value;
setUpdatedQuestion(value);
}}
/>
I tried to add if (event.target.value == "" || event.target.value) to onChange but does not work either
OK I found something, but it is a McGyver tip, not very clean : adding one space before all new add question ahah. But then I can't see my placeholder anymore :/
FYI : question is coming from a questions.map, setUpdatedQuestion do update questions, newOrder also do update questions
//in QuestionsContent.js (questions is a questions tab)
const QuestionsContent = props => {
return (
<form onSubmit={onSubmit}>
{isLoading === false && questions.length > 0
? questions.map((question, i) => {
return (
<div key={i}>
<QuestionLine
{...question}
questions={questions}
setQuestions={setQuestions}
setNewOrder={setNewOrder}
/>
</div>
);
})
: null}
<button
onClick={event => {
event.preventDefault();
setAddQuestion({
question: null,
type: "texte"
});
}}
>
Add a question
</button>
</form>
);
};
//in QuestionLine.js:
const QuestionLine = ({
question,
setNewOrder,
setQuestions,
questions
}) => {
const [updatedQuestion, setUpdatedQuestion] = useState("");
// * UPDATE QUESTION *******************************
useEffect(() => {
if (updatedQuestion !== "") {
const tab = [];
for (let j = 0; j < questions.length; j++) {
if (j === i) {
const newObject = {
question: updatedQuestion,
type: type
};
console.log("adding updatedQuestion ===>", newObject);
tab.push(newObject);
} else {
tab.push(questions[j]);
}
}
setQuestions(tab);
setUpdatedQuestion("");
}
}, [updatedQuestion]);
return (
<div >
{/* QUESTION */}
<input
type="text"
value={question}
placeholder="blabla"
onChange={event => {
event.preventDefault();
const value = event.target.value;
setUpdatedQuestion(value);
}}
/>
</div>
);
};
thanks for your precious help
The problem probably comes from the condition in the useEffect : when you want to delete the last character of the string, the state of updatedQuestion is empty and therefore the condition is not executed

How to display array values in dropdownlist using react js?

I want to display all months in dropdown using react js. Below is my code .I have used moments to fetch all 12 months. I am getting value in console but not in my dropdown.
To display the dropdown values want to use the .
I have used that also. I don't know where I went wrong. Could any one can help me with this? Thanks in advance.
const fareMon = () => {
const months = [];
const monthList = moment.months();
months.push(<option value={monthList} />);
console.log('MONTHS123', monthList);
return months;
};
return (
<div className={styles.formClm2}>
<Form.Group className={styles.formGroup}>
<Form.Label>{t('PaymentPage.lblExpiry')}</Form.Label>
<div className="double">
<Form.Control required as="select" name="startDate">
<option value="">Months</option>
{fareMon()}
</Form.Control>
</div>
</Form.Group>
</div> )
const fareMon = () => {
const monthList = moment.months();
const months = monthList.map(item => (
<option key={item} value={item}>{item}</option>
));
console.log('MONTHS123', monthList);
return months;
};
you need to iterate the monthsList and map each month to a <option>.
You need to iterate over the array of months and set the value in the form controls options element.
Something like this, something simplified.
<Form.Control required as="select" name="startDate">
{months.map(month => (<option key={month.id} value={month.value}>{month.label}</option>))}
</Form.Control>
Key points:
Iterate over a data source of some description.
Ensure each option has a key prop
moment.months() returns an array containing the months name, you don't need to create a new array.
You have to map over the array inside the Select to show up the options.
Example:
const fareMon = () => {
const monthList = moment.months();
return monthList;
};
return (
<div className={styles.formClm2}>
<Form.Group className={styles.formGroup}>
<Form.Label>{t('PaymentPage.lblExpiry')}</Form.Label>
<div className="double">
<Form.Control required as="select" name="startDate">
<option value="">Months</option>
{fareMon().map( month => (<option key={month} value={month}>{month}</option>))}
</Form.Control>
</div>
</Form.Group>
</div>
)

Categories

Resources