ReactJS: Dynamic props in mother component - javascript

So my child component has a function that renders textfields based on state.count and every time a user presses a button the state.count will go plus one. As result, more textfields will render. The textfields has onChange and value that take props. How can I make those props unique for each textfield. Here I am rendering my textfields:
let objects = [];
const teamVelden = () => {
for(let i = 0; i < this.state.count; i++){
objects.push(
<div>
<div className="col s5">
<TextField
id="standard-dense"
label="Teamlid volledige naam"
className={classNames(classes.textField, classes.dense)}
margin="dense"
value={this.props.teamlidValue}
onChange={this.props.onTeamlidChange}
error={this.props.tlnaamError === true ? true:false}
/>
</div>
<div className="col s6">
<TextField
id="standard-dense"
label="Teamlid email"
className={classNames(classes.textField, classes.dense)}
margin="dense"
value={this.props.teamlidEmailValue}
onChange={this.props.onTeamlidEmailhange}
error={this.props.tlemailError === true ? true:false}
/>
</div>
</div>
)
}
return objects;
};
In the mother component i'm currently giving the textfield values like this:
case 1:
return <StapTwee
teamlidValue={this.state.tlnaam}
onTeamlidChange={this.onTeamlidChange}
teamlidEmailValue={this.state.tlemail}
onTeamlidEmailhange={this.onTeamlidEmailhange}
/>;
These values need to get dynamic as well I assume.

Related

Showing Results using child when clicking a button in parent component

I am Trying to build a simple trivia quiz using React and i am stuck on as to how to show the results when the user clicks "Check Answer" Button. I need to change the colors of label reflecting correct and wrong choices.
props ={
questions: arr[str]
answers: arr[arr[str]]
correct-answer: "str"
}
export default function QuestionPage(props){let [showResults,setShowResults] = React.useState(false)let Questions = []
// create 5 questions using the data from props
function getQuestions(){
for (let i =0;i<props.questions.length;i++){
Questions.push(
<Question
key= {nanoid()}
question = {props.questions[i]}
answers = {shuffle(props.answers[i])}
correct_answer = {props.correct_answers[i]}
showResults = {showResults}
/> )
}
return Questions
}
function TotalScore(){
Questions.forEach( (value,index)=>{
console.log(value,"\t",index)
})
//get all inputs using DOM and check
}
return (
<main>
{getQuestions()}
{ showResults && <TotalScore />}
<button onClick={() => setShowResults(true)}>Check Answers</button>
</main>
)
}
I was hoping to change the background color of labels inside the input field I have created for answering the questions :
Red if user selected the wrong option
Green if correct.
I can handle the css bit of coloring, just need to understand "how to implement the passing of command from button in parent component down to child component" functionality in React.
Below is the code for Question.js Component
export default function Question ({question,answers,correct_answer,showResults}) {
const [selected,setSelected] = React.useState({
selected: ""
})
function handleChange(event){
console.log(event.target,"\t",selected.selected)
setSelected( {
selected: event.target.value}
)
}
return(
<div className='question-container'>
<h4>{question}</h4>
<div className='answers-row'>
<fieldset>
<input
type="radio"
id = {answers[0]}
name = {question}
value = {answers[0]}
onChange = {handleChange}
checked = {selected.selected === answers[0]}
/>
<label htmlFor={answers[0]}>{answers[0]}</label>
<br />
<input
type="radio"
id={answers[1]}
name = {question}
value = {answers[1]}
onChange = {handleChange}
checked= {selected.selected === answers[1]}
/>
<label htmlFor={answers[1]}>{answers[1]}</label>
<br />
<input
type="radio"
id={answers[2]}
name = {question}
value = {answers[2]}
onChange = {handleChange}
checked= {selected.selected === answers[2]}
/>
<label htmlFor={answers[2]}>{answers[2]}</label>
<br />
<input
type="radio"
id={answers[3]}
name = {question}
value = {answers[3]}
onChange = {handleChange}
checked= {selected.selected === answers[3]}
/>
<label htmlFor={answers[3]}>{answers[3]}</label>
<br />
</fieldset>
</div>
</div>
)
}
In your code, you are maintaining the state ie. the answer selected by the user, on each Question component. However, when the user clicks the button QuestionPage components re-render so do its children. The getQuestions() will be invoked and components will be created again and states will again be initialized to the default value ie "".
Read this https://reactjs.org/docs/lifting-state-up.html. The example given here is similar to what you are trying to do.

NextJS Dynamic List Not Updating on Delete/Update

Currently, my react/nextjs dynamic list is not updating correctly, I've given an array to map so it'll show a new row on frame update since it's stored on useState, Usually, the issue is with the key of the list that is not unique, but my key is unique
Heres my code
const [allSelectedMaterials, setAllSelectedMaterials] = useState([]) // This variable stores a javascript object into the array
{console.log('-- Updated --')}
{allSelectedMaterials.map((material, i) => {
const key = Object.keys(material).toString()
console.log(`${i} - [${key}] ${material}`)
return (
<div key={key}>
<Row className='align-items-center'>
<Col xs='auto'>
<h6>{material[key].name}</h6>
</Col>
<Col>
<Button variant='danger' className='mb-1' onClick={() => handleDeleteMaterial(key)}>
Remove
</Button>
</Col>
</Row>
<InputGroup>
<InputGroup.Text>
<Image src={material[key].image} className={`${styles.allMaterialsImage}`} />
</InputGroup.Text>
<Form.Control type='number' min={1} ref={(e) => (selectedMaterials.current[i] = e)} required />
</InputGroup>
<div className='mt-1' />
</div>
)
})}
The problem is after I added the first item on the list and adding a new one it won't update the view, here's the console output
And here's me adding a second entry to the list
It clearly says on the log that the array (stored in useState) is updated but it's not changing the view it's still the same as the previous one. But if I reupdate the frame by updating any useState variable it updated the list
Update:
Here's my code for adding new material
(loadedMaterials is just a list of materials that is retrieved from an API)
const handleAddSelectedMaterial = () => {
loadedMaterials.map((material) => {
const key = Object.keys(material)
if (key == currentSelectedMaterial) {
let _material
if (allSelectedMaterials.length > 0) _material = allSelectedMaterials
else _material = []
_material.push({ [material[key].id]: material[key] })
setAllSelectedMaterials(_material)
}
})
}
Try replacing
from
_material.push({ [material[key].id]: material[key] })
setAllSelectedMaterials(_material)
to
_material.push({ [material[key].id]: material[key] })
setAllSelectedMaterials([..._material])
Thank you everyone for the input and responses, but I've managed to solve this issue by redoing how the form & dynamic list work and it's working perfectly now.
If anyone is wondering I just basically follow this person implementation on a dynamic list
https://www.cluemediator.com/add-or-remove-input-fields-dynamically-with-reactjs

Creating a React Component with a click, need to differentiate every Component

I've made such a table to put articles with their respective prices, quantity... What's more there's a button and with every click it's created a new article. (put a photo to see it clearer)
The fact is that I need a way to differentiate every line, what i say is that in the square "Num" there should be 1,2,3... this is very important to me because later I need to send the information to a database, but my problem is I don't know how to differentiate every line. Here I put the code
class LinArt extends Component{
constructor(){
super()
this.state = {
quan:2,
price:20,
dto:10,
count:1
}
this.newArt = this.newArt.bind(this)
}
newArt(){
this.setState({count:this.state.count+1})
}
showArt(){
const total = (this.state.cant * this.state.preu) * (1-(this.state.dto/100));
let lines =[];
for(let i=0; i < this.state.count; i++){
lines.push(
<div key={i}>
<TextField type="number" disabled={true} value={this.state.count} label="Num"/>
<TextField label="DescripciĆ³" variant="outlined"/>
<TextField type="number" label="Cant" value={this.state.quan}/>
<TextField type="number" label="Price" value={this.state.price}/>
<TextField type="number" label="Dto" value={this.state.dto }}/>
<TextField type="number" disabled={true} value={total} label="Total"/>
<TextField type="number" disabled={true} value="21" label="IVA"/>
<br/><hr/>
</div>
)
}
return linies || null;
}
render(){
return(
<div>
{this.showArt()}
<Button onClick={this.newArt}>New article </Button>
</div>
)
}
Thank you for your attention, I appreciate so much your help!
Looking at your code, if you want the num column to increment, you should add i + 1 increment variable as the value to your num field so as it runs the loop, it gives the incremented value and starts from 1 rather than 0. Like so,
<TextField type="number" disabled={true} value={i + 1} label="Num"/>

Finding the value of the element clicked in React

I am using Material Ui for my elements and I have a button which when I click I need to know the value of it so that I can pass it through to the backend to remove it..
My JSX code
for (let i = 0; i < this.state.emails.length; i++) {
emails.push(
<div key={i}>
<TextField style={textField}
autoFocus
floatingLabelText="EMAIL"
type="email"
spellCheck={false}
autoCorrect={"off"}
value={this.state.emails[i]}
onChange={(e) => this.setState({primaryEmail: e.target.value})}
/>
<FlatButton
primary
label="REMOVE EMAIL"
className="userProfile-buttons"
value={this.state.emails[i]}
onClick={this.removeEmailHandler}
/>
</div>
)
}
My js code
removeEmailHandler = (e) => {
console.log(e.target.value)
}
You can change your to
removeEmailHandler = (value) => {
console.log(value)
}
So you can pass in the value in onClick
and onClick={(value)=>this.removeEmailHandler(value) }
You really don't need the value of the button where the removeEmailHandler() method is called. What you need should be in the state, given that it is been set onChange within the TextField component prop.
So the removeEmailHandler() method should basically make an API call with what is in the state to the backend.
removeEmailHandler = (e) => {
// make API call with `this.state.primaryEmail`
}

when second or any other image radio button is clicked, the first one gets updated

I have a form where menu has to be uploaded with menu title and type. The type can be food-menu and beverage-menu. Each type will have only 3 images. I have done that part
but when selecting the radio button for menu type, if i select second or other than first radio button, the first radio button gets selected. How do I solve this issue?
The code can be large so here is the demo
https://codesandbox.io/s/zxxrnw2qx4
here is the code in brief
const Preview = props => {
return (
<div>
{props.files.map((file, index) => {
if (['image/png', 'image/jpg', 'image/jpeg'].includes(file.type)) {
return (
<ClientUploadedImage
key={index}
file={file}
id={index}
menu_title={props.menu_title}
type={props.type}
foodmenuError={props.foodmenuError}
handleChange={props.handleChange}
handleTypeChange={props.handleTypeChange}
/>
);
}
return <div>No File to Show Preview</div>;
})}
</div>
);
};
const ClientUploadedImage = props => {
return (
<section id="menus-photo">
<img src={props.file.preview} width={'400px'} height={'auto'} />
<div className="content">
<form className="form">
<Radio
value="food-menu"
label={`Food Menu${props.id}`}
name={props.id}
handleChangeEvent={props.handleTypeChange}
isChecked={props.type[props.id] === 'food-menu'}
/>
<Radio
value="beverages-menu"
label={'Beverages Menu'}
name={props.id}
handleChangeEvent={props.handleTypeChange}
isChecked={props.type[props.id] === 'beverages-menu'}
/>
<InputField
name={props.id}
type="text"
label="Menu Title"
onChange={props.handleChange}
value={props.menu_title[props.id]}
/>
</form>
</div>
</section>
);
};
Your type array is empty in initial state. If you directly select radio button from second image, handleTypeChange is being called with index 1. In the function, ...types.slice(0, index) becomes types.slice(0,1) which eventually performs spread operation on a blank array and your newValue is appended at 0th position which leads to selection of first image radio button. Here you need to handle the blank array condition for first selection inside the function and you will be good to go.

Categories

Resources