Rendering a dynamic input field in React - javascript

I have a quiz form here and would like to add input fields for questions when a user clicks the "Add a question" button.
I've been playing around with the code and have been able to populate the state object with some text. Obviously, the goal is to have this be an input component and then somehow rendering this to the screen.
What I'm struggling with is how to render an actual element to the page. I know it's done in the render method of the component just not exactly sure how.
I think I'm getting close. Any help would be appreciated. The code is below.
Thanks!
var QuizForm = React.createClass({
getInitialState : function() {
return { questions : [] }
},
createQuestion : function() {
this.state.questions.push("Test");
// Adds "Test" to state object.
this.setState({
questions : this.state.questions
});
},
render : function() {
return (
<div className="quiz-form well text-center">
<h1 className="header text-center">Quiz Form</h1>
<ul>
{/* Would like question inputs to show up here */}
</ul>
<button onClick={this.createQuestion} className="add-question-btn btn btn-primary" style={{ marginTop : 40 }}>Add Question</button>
</div>
);
}

Just map your this.state.questions array to the HTML element you want.
For instance, if you want to render <li> elements:
render : function() {
return (
<div className="quiz-form well text-center">
<h1 className="header text-center">Quiz Form</h1>
<ul> // magic happens now
{this.state.questions.map(function(state) {
return <li>{state}</li>
})}
</ul>
<button onClick={this.createQuestion}
className="add-question-btn btn btn-primary"
style={{ marginTop : 40 }}>Add Question</button>
</div>
);
}
See an example.
If you want to render <input> tags, you can use the same technique above, but be mindful of the fact that React treats it as a controlled component.

A React best practice would be to map your state.questions array to a dynamically generated HTML component such as:
render : function() {
return (
<div className="quiz-form well text-center">
<h1 className="header text-center">Quiz Form</h1>
<ul> // magic happens now
{this.state.questions.map(function(state) {
return <li key={state.someId}>{state.question}</li>
})}
</ul>
<button onClick={this.createQuestion}
className="add-question-btn btn btn-primary"
style={{ marginTop : 40 }}>Add Question</button>
</div>
);
}
Please keep in mind that when mapping and rendering dynamic objects in React it's always good to insert a key for each mapped object. So make sure to create that when you're creating the content.
Best Regards,

Related

All buttons effect only the first button, but not themselves

card.vue is the template with the toggleLike method and the button tag
<template>
<div
class="p-24 grid grid-cols-1 sm:grid-cols-1 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 gap-12"
>
<div
v-for="movie in movies"
:key="movie.imdbID"
>
<card :movieData="movie" />
</div>
</div>
</template>
I have dynamically generated buttons, but whichever I click, every time the first one gets "selected" or diselected (depends on its previous state).
I want to toggle them independently.
<button #click="toggleLike()" class="mr-2">
<i id="but" class="fa-thumbs-up" :class="{ fas: isLiked, far: !isLiked }"></i>
toggleLike() {
if(this.movieData.likes == 0) {
$('#but').removeClass('far');
$('#but').addClass('fas');
this.movieData.likes+1;
}
else {
$('#but').removeClass('fas');
$('#but').addClass('far');}
return this.$store.dispatch("updateMovieLikes", {
imdbID: this.movie.imdbID,
});
},
You should put more code on how you structure your generated buttons, is that an array? If it is then your isLiked should be an array
<div v-for="movie in movies">
<button #click="toggleLike(movie)" class="mr-2">
<i id="but" class="fa-thumbs-up" :class="movie.likes === 0 ? 'fas' : 'far'"></i>
</button>
</div>
methods: {
toggleLike(movie) {
movie.likes = movie.likes === 0 ? 1 : 0;
this.$store.dispatch("updateMovieLikes", {
imdbID: this.movie.imdbID,
});
}
}
Edit: Just a tip you dont need jquery in vue
Welcome to SO, You could actually shift-away from jquery with vue. You already made a dynamic class binding on your but element therefore you can remove the jquery class handling on your method.
template:
<button #click="toggleLike()" class="mr-2">
<i class="fa-thumbs-up" :class="isLiked ? 'fas' : 'far'"></i>
</button>
method:
toggleLike() {
// Add here how you handle the toggling of isLiked data
if (this.movieData.likes === 0) this.movieData.likes++;
return this.$store.dispatch("updateMovieLikes", {
imdbID: this.movie.imdbID,
});
},
Note.
I'm not sure how you generate the buttons dynamically, can you show it on your question? (can't comment yet). You should also show how do you handle the toggling of your isLiked data.

onChange for input React JS makes typing render slow

I am making a React page which has a post and comments on the post. Now the onChange on being triggered re-renders the whole class which makes the typing in input slow.
Now, if the is declared in a separate class and the value entered in the input there can be sent to the main class for API call. But I am not able to do this. Can anyone help me?
Below is code for my comment section of the screen.
commentChange(html) {
this.setState({ post_comment: html})
}
<div className="post-comments">
<div className="post-comments-head">
<div>Comments ({this.state.comments.length})</div>
</div>
<div className="comments">
{this.createCommentList(this.state.comments2)}
</div>
</div>
<div className="post-commenting">
{this.state.reply == -1 ? <span>Comment as {this.state.name}</span>
: this.commentBy()}
<div className="write-comment-post">
<ReactQuill
data-gramm_editor="false"
onChange={this.commentChange}
value={this.state.post_comment}
className="post_comments_x"
placeholder="Write a comment"
ref={(ip) => this.myInp = ip}
autoFocus={true}
theme=""
/>
<div className="comments-submit">
<button className="submit-comment"
onClick={() => this.submitComment(this.state.reply)}
disabled={!enabledComment}>
Comment
</button>
</div>
</div>
</div>
The createCommentList function takes comments and returns a nested list of comments. Below is the section where new comment is added.
How to solve this because it is making typing a new comment very slow.
<div className="write-comment-post">
<ReactQuill
data-gramm_editor="false"
onChange={this.commentChange}
value={this.state.post_comment}
className="post_comments_x"
placeholder="Write a comment"
ref={(ip) => this.myInp = ip}
autoFocus={true}
theme=""
/>
create a seperate component for this
and only onn enter call parennt function else onchange it will trigger

The generated div table does not render

I am doing a React web app and trying to dynamically generate table based on the selected data (users from a time period). The user data is downloaded successfully. I am using the same approach in other page. However, this time it does not render.
Here is the code:
displayUsers(tableOfUsers) {
let table = tableOfUsers.map(user => {
return (
<div className="div-table-row-titles" key={user.name}>
<div className="div-table-col">{user.name}</div>
<div className="div-table-col">{user.surname}</div>
<div className="div-table-col">{user.email}</div>
<div className="div-table-col">{user.tel}</div>
<div className="div-table-col">{user.addedBy}</div>
</div>
);
});
return table;
}
render() {
const userData = null;
if (this.state.volunteers !== []) {
console.log("HELLO");
let userData = this.state.volunteers.map(user => {
return (
<div className="div-table-row-titles">
<div className="div-table-col">{user.name}</div>
<div className="div-table-col">{user.surname}</div>
<div className="div-table-col">{user.email}</div>
<div className="div-table-col">{user.tel}</div>
<div className="div-table-col">{user.addedBy}</div>
</div>
);
}); // this.displayUsers(this.state.volunteers);
console.log(userData);
userData = userData[0];
}
return (
<form id="form1">
<div>
<p>{this.props.startTime}</p>
</div>
<div className="div-table">
<div className="div-table-row-titles">
<div className="div-table-col" align="center">
Name
</div>
<div className="div-table-col" align="center">
Surname
</div>
<div className="div-table-col" align="center">
Email
</div>
<div className="div-table-col" align="center">
Tel. No.
</div>
<div className="div-table-col" align="center">
Volunteer
</div>
</div>
{userData}
</div>
</form>
);
}
The user data is there, it is properly ordered, the userData is not null (it is detected as [{...},{...}]). Yet, it does not display. Any ideas how could I fix it?
Thanks in advance!
------SOLUTION-------
I have found the problem. The user.addedBy was an object (addedBy had other properties), therefore React could not process it. Solved!
Without access to the project this is a bit challenging to debug. I would start by simplifying the problem.
First maybe look at what you are trying to accomplish and break out what can be reusable components (Cell, Row, Column, Columns, etc)
Start building each section one at a time so you can debug each part one step a time, such as if you have 5 volunteers can you get 5 rows to show up?
Can I ask why you are using divs instead of a table?
Is this issue due to scoping of let ??
Since let is block scoped and "let userData" is within the if block, it won't be available outside {}.
Could you please try declaring userData outside the if scope or using var??

Render 2 div's in one and make to hidden and to appear in React

I am new in world of coding and i have a problem with my React code.
What is on my code is a search bar. I want when i click on the search button to appear a text area to put some text and when i press X to display the first button(search). First time i use getInitialState with 2 renders(renderOpen and renderNormal) and works! But now i want to make it all in one render and to display it. The code is propretly good but is displaying
2 object and i want to display all in one object.
Thanks!!!
import React from 'react';
import ReactDOM from 'react-dom';
var Button = React.createClass({
getInitialState: function() {
return {search:false}
},
close: function(){
this.setState({search:false})
console.log('test1');
},
open: function(){
this.setState({search:true})
console.log('test2');
},
renderNormal: function (){
return(
<div className="mainDiv">
<div className="search-close">
<img src="search2.png" className="search-button" alt="" onClick={this.open}/>
<layer onClick={this.open} className="layer">
<span className="defaultText">Search
</span>
</layer>
</div>
<div className="search-open">
<span className="layer2">
</span>
<input type="image" src="search2.png" className="search-button2"/>
<input type="text" placeholder=" Search" className="text-area"/>
<span className="close-area" onClick={this.close}>x
</span>
</div>
</div>
)
},
render: function() {
if(this.state.search) {
return this.renderNormal(
this.props.search-close);
}else {
return this.renderNormal(
this.props.search-open);
}
}
});
const element = <div>
<Button/>
</div>;
ReactDOM.render(
element,
document.getElementById('root')
);
You want renderNormal to return just one of the two, like this:
renderNormal: function (){
if(this.state.search) {
return(
<div className="mainDiv">
<div className="search-open">
<span className="layer2">
</span>
<input type="image" src="search2.png" className="search-button2"/>
<input type="text" placeholder=" Search" className="text-area"/>
<span className="close-area" onClick={this.close}>x
</span>
</div>
/div>)
}
else {
return (
<div className="mainDiv">
<div className="search-close">
....
</div>
/div>)
}
}
With a bit of effort you could avoid repeating the mainDiv container, but the key lesson here is that the render method is pure Javascript, and you can use if statements inside of it. The JSX code that looks like HTML is actually just a special syntax for making Javascript objects... which will be used by the top levels of React to manipulate the DOM just like the HTML. (Well, not quite, but pretty close.)

Could not display not found information using conditional syntax inside jsx

I have a searching system where user search by typing the place. If the place does not match, it should show not found and if place matches, it should show the place detail. What I did is
code
render() {
var margin = { marginTop : '13em' };
if (this.state.place){
let location = _.map(this.state.place, (place,id) => {
return(
<Room key={id}
slug={place.slug}
place={place.place}
city={place.city}
gallery={place.gallery}
property={place.property}/>
)
console.log('location',location);
});
let gallery = _.map(this.state.place, (place,id) => {
console.log('place',place.gallery);
_.map(place.gallery, (image,id) => {
return(
<img src={image.image} class="img-fluid" />
)
});
});
let noLocation = () => {
return(
<div className="noroom">There is no room</div>
);
console.log('nolocation');
};
return(
<div className = "container">
<div className="content text-align-center">
<div className="row text-xs-center">
<div className="middle-text" style={margin}>
<h1 className="welcome"><span>Common Rental Space</span></h1>
<p className="appSubtitle">facilitates your search for rental space all over Nepal</p>
<button ref="test" className="btn how-it-works" onClick={this.handleClick}>Search Space</button>
</div>
</div>
</div>
<div id="mySearch" className="overlay" onKeyDown={this.handleKeyDown}>
<button className="btn closebtn" onClick={this.handleClick}>x</button>
<div className="overlay-content">
<SearchInput ref="searchInput" className="search-input" onChange={this.searchUpdated} />
<div className="container searchList">
{ this.state.place > 1 ? {location} : {noLocation} }
</div>
</div>
</div>
</div>
);
}
}
}
What might be the error? The syntax is { condition ? true : false }
When I do {this.state.place >1 ? { location } : {noLocation} } I get an error
app.js:1030 Uncaught Invariant Violation: findComponentRoot(..., .0.1.1.1.0.0): Unable to find element. This probably means the DOM was unexpectedly mutated (e.g., by the browser), usually due to forgetting a <tbody> when using tables, nesting tags like <form>, <p>, or <a> or using non-SVG elements in an <svg> parent. Try inspecting the child nodes of the element with React ID ``.
When I do this.state.place >1?{location}:{noLocation}i get this.state.place >1 ? result, and if place does not match the page shows this.state.place >1 ? :.
noLocation appears to be a method, but you aren't actually calling the method anywhere - so you are basically telling reaction to render the function itself, not the result of the method.
If it must be a method, try, noting the extra ():
{ this.state.place.length >=1 ? location : noLocation() }

Categories

Resources