I am trying to create a dynamic element. whatever user input in an input box, the same number of LI element will create.
I get this output when I enter 2 in input box.
<li key=0>input 0</li><li key=1>input 1</li>
where I want two li elements.
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
times: 0
};
}
createList = event => {
let times = event.target.value;
this.setState({
times
});
};
getData = times => {
let str = "";
for (let i = 0; i < times; i++) {
str += "<li key=" + i + ">input " + i + "</li>";
}
return <ul>{str}</ul>;
};
render() {
return (
<div className="App">
<p className="App-intro">
<input type="number" onChange={this.createList} />
{this.getData(this.state.times)}
</p>
</div>
);
}
}
export default App;
This is because you are treating them as string instead of DOM node elements. Use an array and push all the elements inside that and return the final result.
Don't forgot you are writing JSX, and <li> input: {i+1} </li> will get converted into:
React.createElement(
"li",
null,
"input: ",
i+1
);
But if you write: "<li>input: {i+1}</li>", it will be treated as a string.
Check the complied code result of these two cases by Babel REPL.
Solution:
Write it like this:
getData = times => {
let str = [];
for (let i = 0; i < times; i++) {
str.push(<li key={i}> input: {i+1} </li>);
}
return <ul>{str}</ul>;
};
Related
I try to append a component dynamically, here it's Boards that i try to add to MainComponent.
The function addChild work and the list boards is filling up when the loop for is running, but nothing else happen, nothing is displayed on the screen
const MainComponent = props => (
<div className={props.className}>
<p>
<a href="#" onClick={props.addChild}>Add Another Child Component</a>
</p>
{props.boards}
</div>
);
class MainPage extends React.Component {
state = {
numChildren: 0
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
});
}
render () {
const boards = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
var _id = "board-" + i;
console.log
boards.push(<Board id={_id} className="board" />);
};
return (
<div className="test">
<Title/>
<MainComponent addChild={this.onAddChild} className="flexbox">
{boards}
</MainComponent>
</div>
);
}
}
export default MainPage```
Try to add key for each ChildComponent
const boards = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
var _id = "board-" + i;
console.log
boards.push(<Board id={_id} key={_id} className="board" />);
};
because react depends on the key for rendering the component that is rendered inside loops
so react considers that all child that you are rendering is one component unless you give them a different key
I am relatively new to React so sorry if this is a really easy/stupid question.
So I am making an app where you provide a number of months and depending on the number of months that number of text fields need to show up. Ex. 5 months = 5 new text fields. How would I go about doing that? I have tried various methods to no avail hence why I am asking here. Thank you for your help.
class MonthlyInputs extends React.Component {
render() {
let monthInputs = [1, 2, 3];
let items = [];
function updateInputs(event) {
monthInputs = [];
for (let i = 0; i < event.target.value; i++) {
monthInputs.push(i);
}
console.log(monthInputs)
for (let i = 0; i < monthInputs.length; i++) {
items.push(<li key={i}>list item {i}</li>);
}
}
for (const [index, value] of monthInputs.entries()) {
items.push(<li key={index}>{value}</li>)
}
return (
<div>
<input onChange={updateInputs} />
{items}
</div>
)
}
}
I attempted what you're trying to implement.
Let me know if you have any questions.
https://codesandbox.io/s/tender-liskov-iicor?fontsize=14&hidenavigation=1&theme=dark
Consider saving your variables in state. Also you shouldn't declare funcions in render funcion. Here is code:
class App extends Component{
constructor(){
super();
this.state = {
monthInputs: [],
}
}
updateInputs = (event) => {
let monthInputs = [];
for (let i = 0; i < event.target.value; i++) {
monthInputs.push(i);
}
this.setState({
monthInputs
})
}
render(){
const {monthInputs} = this.state;
return(
<div>
<input type="number" value={monthInputs} onChange={this.updateInputs} />
{monthInputs.length > 0 && monthInputs.map((number, index)=>
<li key={number}>{number}</li> )}
</div>
)
}
}
And here is link to codesandbox
I am creating an app, where users should compose a correct sentence from shuffled one. Like on the following picture. The problem is that I am not implementing the app in a right way. For example, I do not change inputs through state. And my main question is how to clear input of a particular line (a bunch of inputs) ? I need it when a user types something wrong and needs to start again. I know that input field should be controlled through state, but it is impossible since all inputs are generated according to number of words. And in one line there maybe more than one input. Let me explain how my code works, so that you have an idea my implementation.
This is where I get data from.
export default {
id:'1',
parts:[
{
speaker:'Speaker1',
words:'Hello how are you?'
},
{ speaker:'Speaker2',
words:'I am OK, thanks'
},
{ speaker:'Speaker1',
words:'What are your plans for Saturday?'
}
]
}
import React, { Component } from 'react';
import {CopyToClipboard} from 'react-copy-to-clipboard';
import { MdDoneAll } from "react-icons/md";
// This is the array where data from inputs is pushed to
var pushArr = [];
// Initial points
var points = 0;
class DialogueShuffleFrame extends Component {
constructor(props) {
super(props)
this.state = {
// Variable to show correct or incorrect notification
showCorrect:false
}
this.writeSometihng = this.writeSometihng.bind(this)
}
// Function that is triggered when a tick is clicked
// Pushes value to the pushArr array when onBlur function is triggered
writeSometihng(e) {
e.preventDefault()
pushArr.push(e.target.value)
console.log(pushArr)
}
// Function check if array of value from input matches to an array from
// initial data source
checkLines(arr, lines) {
let joinedStr = arr.join(' ');
//console.log(joinedStr);
lines[0].parts.map((obj) => {
let line = obj.words
if (joinedStr === line) {
this.setState({
showCorrect:true
})
pushArr = [];
points += 80;
} else {
pushArr = [];
}
})
}
// Resets pushArr array
reset() {
pushArr.length = 0
console.log('clicked')
}
// Shuffles words
formatWords(words) {
const splittedWords = words.split(' ')
const shuffledArray = this.shuffle(splittedWords)
return (
shuffledArray.map((word, index) => (
<>
<input className="word-to-drop-input" id={index} onBlur={this.writeSometihng} size={2} />
<CopyToClipboard text={word}>
<span key={uid(word)} value={word} className="word-to-drop">{word}</span>
</CopyToClipboard>
</>
))
)
}
shuffle(a) {
var j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = a[i];
a[i] = a[j];
a[j] = x;
}
return a;
}
render() {
const {lines} = this.props
const shuffles = lines[0].parts && (
lines[0].parts.map((element,i) => (
<>
<li className="line" key={i}><span>{element.speaker}{": "}</span><span>{this.formatWords(element.words)}</span></li>
<MdDoneAll type="button" onClick={() => {this.checkLines(pushArr, lines)}} style={{color:'white'}}/>
</>
))
)
return (
<>
<h1 className="centered" style={{color:'white'}}>Dialogue shuffle frame</h1>
<ul className="lines-container">
{shuffles}
</ul>
{<div className="reactangular">{this.state.showCorrect ? 'Correct' : 'Incorrect'}</div>}
<div>{points}</div>
<div className="reactangular" onClick={() => this.reset()}>Reset</div>
</>
)
}
}
export default DialogueShuffleFrame;
I'd recommend to use proper data structure with state management, but that's a different story.
To solve your specific issue of clearing input elements, you can use ReactDOM.findDOMNode to access the DOM node and traverse the input elements and set the value to empty string.
Something like this:
class App extends React.Component {
check = (ref) => {
const inputElements = ReactDOM.findDOMNode(ref.target).parentNode.getElementsByTagName('input');
[...inputElements].forEach(el => el.value = '')
}
render() {
return (
<div>
<ul>
<li>
<input />
<input />
<button onClick={this.check}>Check</button>
</li>
<li>
<input />
<input />
<input />
<button onClick={this.check}>Check</button>
</li>
</ul>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
I'm trying to do something very simple but its not playing well with my code. I can see it render but only 3 times and not 9
const renderTempBoxes = () => {
for (var i = 0; i < 10; i++) {
console.log('i = ', i);
return <div className={styles.box} key={i} />;
}
};
const Component = () => {
return (
{renderTempBoxes()}
)
}
This doesn't even work, which is overkill to use an array when I just want 9 boxes to render.
UPDATE:
const Component = () => {
return (
<div>
{
[...Array(10)].map((x, i) => {
console.log('i = ', i);
return <div className={styles.box} key={i} />;
})
}
</div>
)
}
The first issue is that you simply cannot return individual elements from within the for loop like that. This is not specific to React, this is simply a JavaScript issue. Instead you can try something like this using Array.from to map an array of elements:
const renderTempBoxes = () => Array.from({ length: 10 }).map((v, i) =>
<div className={styles.box} key={i}>{i}</div>
);
Or simply the for loop with Array.prototype.push to generate an array of elements and return it:
const renderTempBoxes = () => {
let els = [];
for (let i = 0; i < 10; i++) {
els.push(<div className={styles.box} key={i}>{i}</div>);
}
return els;
};
Rendering the elements:
const Component = () => {
return (
<div>
{renderTempBoxes()}
</div>
)
}
Or with React.Fragment to forgo the wrapping extra node:
const Component = () => {
return (
<React.Fragment>
{renderTempBoxes()}
</React.Fragment>
)
}
The second issue with your example is that <div /> isn't going to really render anything, it's not a void/self-closing element such as <meta />. Instead you would need to do return the div element as <div className={styles.box} key={i}>{whatever}</div>.
Regarding the syntax [...Array(10)], there must be an Webpack in terms of how it handles/transpiles Array(10), [...Array(10)], [...new Array(10)], or even `[...new Array(10).keys()]. Either of the approaches described in the answer should solve your issue.
I've created a StackBlitz to demonstrate the functionality.
When trying to render multiple times the same components use an array an map over it.
export default class MyComp extends Component {
constructor(props) {
super(props)
this.state = {
array: [{key: 1, props: {...}}, {key: 2, props: {...}, ...]
}
}
render () {
return (
<div>
{this.state.array.map((element) => {
return <div key={element.key} {...element.props}>
})}
</div>
)
}
}
Remember to always set a unique key to every component you render
class AddForm extends Component {
render() {
return (
<div>
asd
</div>
);
}
}
let addComponents = (num) => {
let i;
for(i = 0; i < num; i++) {
return (<AddForm />);
}
}
<div className="add">{ addComponents(this.state.addNum) }</div>
i want append asd * num
but asd is only one
this.state.addNum = 1
how react append asd * num?
Sorry, i am bad at english
Your function addComponent returns a single component, while it should actually return an array of components.
const addComponents = (num) => {
let arr = [];
for(let i = 0; i < num; i++) {
arr.push(<AddForm />);
}
return arr;
}
I am hoping that you want an output something like this..
if num = 3 then the output should be "asd asd asd"
The code for this will be :
class AddForm extends Component {
render() {
var str = "asd ";
for(var i=1; i<this.props.num; i++) {
str = str+ "asd ";
}
return (
<div>
{str}
</div>
);
}
}
let addComponents = (num) => {
return (<AddForm num={num} />);
}
<div className="add">{ addComponents(this.state.addNum) }</div>
If you are looking for an output something like this.. (asd * 3) if num = 3 then use the below code..
class AddForm extends Component {
render() {
return (
<div>
asd * {this.props.num}
</div>
);
}
}
let addComponents = (num) => {
return (<AddForm num={num} />);
}
<div className="add">{ addComponents(this.state.addNum) }</div>
In both the cases, you will understand that you need to pass the props to the child element and let the child element decide what it wants to render.
If you want the output something like this..
<div>asd</div>
<div>asd</div>
<div>asd</div>
Then the answer posted above by OB3 is correct.