ReactJS | Concat Arrays Inside Of Object - arrays

im currently working on a React JS App and i have an Array which has Objects in it, and inside the Objects it has Arrays.
Here's the code first,
constructor() {
super();
this.state = {
namaSantri: null,
examTitle: 'PLP BAHAS Arab Talaqqi',
examLevel: 0,
examType: 'Ujian 1 Tulis',
vocabQ: [{questionTitle: 'Question Title', question: ['Question 1', 'Question 2']}],
};
}
componentDidMount(){
var questionEx = {questionTitle: 'Question Title2', question: ['Question 1']}
var anotherArray = ['Question 2']
var addIndex = questionEx.question.concat(anotherArray)
this.setState({
vocabQ: [...this.state.vocabQ, addIndex]
})
}
So i have an Array whis is vocabQ here, and it contains Objects which contains my QuestionTitles and my Questions Array.
I want to create an input program for the Quesiton Object ( that contains questionTitle and questions ) here, so i tried to concat my array at addIndex but it show nothing. Help?
My render,
render() {
/**SOMETHING THAT YOU NEED TO WRITE TO OUTPUT AN ARRAY? */
const map = this.state.vocabQ.map((d) => <p key={d.questionTitle}>{d.questionTitle} {d.question}</p>);
return (
<div /**DIV PAGE START */
className="App">
<div /**DIV HEADER START */
className="DivHeader">
<Center>
<p className="ExamTitle">{this.state.examTitle}</p>
</Center>
<Center>
<p className="ExamLevel">Level {this.state.examLevel}</p>
</Center>
<Center>
<p className="ExamType">{this.state.examType}</p>
</Center>
<Center>
<div className="NameInput">
<InputText value={this.state.namaSantri} onChange={(e) => this.setState({value: e.target.namaSantri})} />
{/**HERE IS WHERE THE ARRAY SHOULD BE */}
<span> {map}</span>
</div>
</Center>
{/**DIV HEADER END */}
</div>
<div /**DIV VOCAB QUESTIONS START */>
{/**DIV VOCAB QUESTIONS END */}
</div>
{/**DIV PAGE END*/}
</div >
);
}
NB: It only show "Question Title Question 1Question 2"
image

Try it like this:
componentDidMount(){
var questionEx = {questionTitle: 'Question Title2', question: ['Question 1']}
var anotherArray = ['Question 2']
var addIndex = questionEx.question.concat(anotherArray)
this.setState({
vocabQ: [...this.state.vocabQ, ...addIndex] //<----This "..." is the part that has changed
})
}
You basically have to use the spread operator on both arrays in order for it to work the way you want to. Head to https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/Spread_operatorhttps://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/Spread_operator to learn a bit more about that.

Related

"Fill" in Data to one Template Page

Im looking for a solution to build a Template website in React which displays a Carousel with 3 different Images on the Left Part and a Informational Part on the Right with A Title, some Attributes and a Description. I dont want to Copy and Paste the Website all the Time to rewrite every element and Change the Pictures. The Barebone of my Page is done, i just need the Solution to probably make an Array and depended on the Image which is clicked, youre getting to the mentioned DescriptionPage filled with the Information based on the clicked Image.
const DescriptionPage = () => {
return (
<>
<div className='CardDescriptionContainer'>
<Carousel>
<CarouselItem><img alt="Fool" className='image-carousel' src={Fool} draggable="false"/></CarouselItem>
<CarouselItem><img alt="Fool" className='image-carousel' src={ThothFool} draggable="false"/></CarouselItem>
<CarouselItem><img alt="Fool" className='image-carousel' src={JDFool} draggable="false"/></CarouselItem>
</Carousel>
<div className='CardDescription '>
<div className='Title'>
<h1>The Fool</h1>
</div>
<div className='Element'>
<h2>Element</h2>
<p>Air</p>
</div>
<div className='Zodiac'>
<h3>Zodiac / Planet</h3>
<p>Uranus</p>
</div>
<div className='Qualities'>
<h3>Qualities</h3>
<p>Freedom <br/>
Lust for Life<br/>
Beginnings<br/>
Adventure</p>
</div>
<div className='Symbols'>
<h3>Symbols</h3>
<p> White Rose <br/>
Small Bundle<br/>
Small Animal<br/>
Precipice</p>
</div>
<div className='ShortDescription'>
<h2>Description</h2>
<p>Into the Unkown.... this is placeholder Text for the Fool as he is, a Placeholder, the nothing before there is with all the Options to go anywhere and be anyone he'd like to. Pure AIR!</p>
</div>
</div>
</div>
</>
)
}
export default DescriptionPage
I was trying to find the Correct Syntax and was thinking about Mapping over an Array containing the Data but i dont know how to choose the correct Array or Dataset based on the clicked Image. Would i use useState to change the State of the Page itself or is it the best approach to "load" the Description Page with the Data stored in a different file?
You can write like this, if I understand you correctly
const INTIAL_VALUES = [
{
className: "Title",
heading: "The Fool",
paragraph: ""
},
{
className: "Element",
heading: "Element",
paragraph: "Air"
}, {
className: "Zodiac",
heading: "Zodiac / Planet",
paragraph: "Uranus"
}, {
className: "Qualities",
heading: "Qualities",
paragraph: "Freedom Lust for Life Beginnings Adventure"
}]
const DescriptionPage = () => {
const [state, setState] = useState(INTIAL_VALUES)
return (
<>
<div className='CardDescriptionContainer'>
<Carousel>
<CarouselItem><img alt="Fool" className='image-carousel' src={Fool} draggable="false" /></CarouselItem>
<CarouselItem><img alt="Fool" className='image-carousel' src={ThothFool} draggable="false" /></CarouselItem>
<CarouselItem><img alt="Fool" className='image-carousel' src={JDFool} draggable="false" /></CarouselItem>
</Carousel>
<div className='CardDescription '>
{state.map((element, key) => (<div key={key} className={element.className}>
<h3>{element.heading}</h3>
<p>{element.paragraph}</p>
</div>))}
</div>
</div>
</>
)
}
export default DescriptionPage

Basic usage of keys - Extract single item prop from data list

In React I have an array of projects (only 2 in this example). Each project has:
key
name
info
I would like to be able to render, for example, only one project name. Everything I have tried gives me the whole list. I am confused on the format I need to use to:
Set up my ID
Tell React what specific ID I want to render
I have tried creating different formats (Try number 1 & 2) using .map
function ProjectList() {
const projects = [
{
id: 0,
name: "Shop",
date: "2019",
info: "info of 1",
},
{
id: 1,
name: "Hotel",
date: "2019",
info: "info of 2",
},
]
const findProjectInfo = projects.map (project =>(
<div key={1}>
<h2 key={project.ID}>
{project.info}
</h2>
</div>
))
return (
<div>
<div className='TRY NUMBER ONE' key={1}>
{projects.map (project=> (
<div key={project.ID}>
<div> {project.name}</div>
<div> {project.info}</div>
</div>
))}
</div>
<div className='TRY NUMBER TWO' key={1}>
{findProjectInfo}
</div>
</div>
)
}
export default ProjectList
Everything I tried gives me the two items of my list and not only the 1st one as I would want.
Try something like that:
const projects = [
{
id: 0,
name: "Shop",
date: "2019",
info: "info of 1",
},
{
id: 1,
name: "Hotel",
date: "2019",
info: "info of 2",
},
];
function ProjectList() {
const [selectedProject,setSelectedProject] = React.useState(0);
return (
<React.Fragment>
<div>Select Project</div>
<button onClick={()=>setSelectedProject(0)}>Project 1</button>
<button onClick={()=>setSelectedProject(1)}>Project 2</button>
<br/><br/>
<div><b>Project name: </b>{projects[selectedProject].name}</div>
<div><b>Project date: </b>{projects[selectedProject].date}</div>
<div><b>Project info: </b>{projects[selectedProject].info}</div>
</React.Fragment>
);
}
ReactDOM.render(<ProjectList/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
.map will always return you an array with the same size as the original. What you want to do is find a specific element, like this:
const project1 = findProjectInfo.find(project => project.id === 1);
This will return a single object instead of an array, which you can then render:
<div>
<div>{project.name}</div>
<div>{project.info}</div>
</div>
More info on Array.prototype.find() here.
The key prop is not to be used for anything related to this. It is a special prop that helps React know when components change, and only usually needed when you want to render arrays (like when you use .map), or in a few more advanced cases.
You are simply iterating through your array, that is why you are getting every element from array as result.
You need to first filter your array using provided ID and then you can iterate using map,
{projects
.filter(projects => projects.id === providedID) //providedID can be stored in state
.map(project => (
<div key={project.id}>
<h2 key={project.id}>{project.info}</h2>
</div>
))
}
Demo
Note: Don't use key like this,
<div className='TRY NUMBER ONE' key={1}>
Also make sure you don't repeat the key for multiple elements.
Update
Changing state and getting project details based on selected project ID.
Demo1

push elements selected into one object

I have a component called "itemSelection" and another one called "Item"
in the itemSelection i just map through an api response like this
<div className="row">
{this.state.items.map(i => <Item name={i.name} quantity={i.quantity} />)}
</div>
in the "Item" component I have like 3 card item with a select button. in its state there is also "quantity" key but that's for the quantity selected by the user of a specific item. So, what I'm try to achieve here is if the user selected one item and quantity of 2, I want to take that in an object and put that object in an array, and if the user selected another item with quantity of 3, I want that in another object and just push that object in the array where I have put the first object to be something like that
[{name: first item, quantity: 2}, {name: second item, quantity: 3}]
here is what I tried
targetValue = (e) => {
e.preventDefault();
let qua = e.target.textContent;
this.setState({quantity: qua, selected: true});
const newQuantity = {name: this.props.name, quantity: qua}
const quantities = [...this.state.quantities];
quantities.push(newQuantity);
this.setState({quantities});
console.log(quantities);
}
The function above is included in the "Item.js" and here is the return function
<div className="col-md-4">
<div className={"card " + (this.state.selected ? "frame" : "")} style={{width: 18+'rem'}}>
<img className="card-img-top" style={{width: 10+'rem', margin: 0+' '+'auto'}} src={this.props.img} alt="Card image cap"/>
<div className="card-body">
<h5 className="card-title">{this.props.name}</h5>
<p clasNames="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit</p>
<div className="description">
<p className="card-text">30€</p>
<p className={"card-text " + (this.state.selected ? "" : "displayQua")}>Q: {this.state.quantity}</p>
</div>
<button onClick={this.toggleMenu} href="#" style={{width: 100 + '%', margin: 0+' '+'auto'}} className="btn">SELECT</button>
</div>
</div>
<div className="menu">
<div className={this.state.visible ? "" : "visible"}>
<div className="menu">
{_.times(this.props.quantity, i => (
<a onClick={this.targetValue} key={i} href="#">{i + 1}</a>
))}
</div>
</div>
</div>
</div>
The question you have asked is a bit unclear. But as far as I understand there could be 2 issues :
Change const quantities = [...this.state.quantities]; to let quantities = [...this.state.quantities]; since you'll be pushing new values to the array.
let qua = e.target.textContent; not sure if this is the best way to get the value. Try let qua = e.target.value; and pass the value param in anchor tag <a onClick={this.targetValue} value={i+1} key={i} href="#">{i + 1}</a>
Sharing the output for what you tried will definitely help to understand better.
The best way to push elements to a state array in React. You can do this
const newQuantity = {name: this.props.name, quantity: qua};
this.setState(previousState => ({
quantities : [...previousState.quantities, newQuantity]
}));
Also don’t use multiple setStates inside handler function use one that’s enough :)

Unable to access Children from json , react-redux

Hi, I'm able to access the contact name Ron and use filter in it. But, I'm unable to access the children contact Alex and use it in filter. I have attached my output screenshot. I need to print children Alex also in it and use filter. Thank you in Advance
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
let arr = [{"id":1,
"number":true,
"location":"2",
"time":11,
"code":1001,
"name":"Ron",
"children":[{
"id":141,
"number":true,
"location":1,
"time":1504969439000,
"code":null,
"name":"Alex"}]}]
const Tab = ({ value }) => <div> {value} </div>;
class App extends React.Component {
constructor() {
super();
this.state ={
search: ''
};
}
updateSearch(event){
this.setState({search: event.target.value.substr(0,20)})
}
render() {
let filteredContacts = arr.filter((item)=>{
return item.name.toLowerCase().indexOf(this.state.search)!==-1;
});
return(<div>
<h1> Contact List </h1>
<input type="text"
value={this.state.search}
placeholder="Search Here"
onChange={this.updateSearch.bind(this)} />
<ul>
{
filteredContacts.map((obj, i) =>
<Tab value={obj.name} key={i} />)
}
</ul>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('container'));
You filtering only goes through the first level of contacts in the array, so any contacts in .children will never be shown or be filtered against.
What you want to do is make an array that contains all the contacts, but have them at the same "bottom" level ([{name: 'Ron'}, {name: 'Alex'}]), so that we can filter on that array instead.
One way of doing this is to first .map over the original array and make sure we get both the "bottom" (parent) contact and the children in one array: (if the ... syntax is new to you, scroll to 'Spread in array literals' here)
const allContacts = originalContacts.map(parentContact => [parentContact, ...parentContact.children])
But that gets us an array of arrays: [[{name: 'Ron'}, {name: 'Alex'}]], which is hard to work with. So we want to make a new array where we have concatenated all the arrays in the allContacts array, into just one. Luckily, there is a way to call the .concat method that takes arguments in an array. It looks a bit odd, but we can do: (more about .apply)
const allContactsFlattened =
Array.prototype.concat.apply([], allContacts);
So now we have what we wanted: a "flat" (one level) array of all the contacts. Your filter method already knows how to handle that, so no further changes has to be made.
To see it in action I included your code with the changes applied (in a slightly different way) + some comments:
let originalContacts = [
{"name":"Ron (parent)", "children":[{"name":"Alex (child)"}, {"name":"Emma (child)"}]},
{"name":"Isa (parent)", "children":[{"name":"Sarah (child)"}, {"name":"Ahmad (child)"}]}
];
const Tab = ({ value }) => <div> {value} </div>;
class App extends React.Component {
constructor() {
super();
this.state ={
search: ''
};
}
updateSearch(event){
this.setState({search: event.target.value.substr(0,20)})
}
render() {
let contactsAndSubContacts =
// flatten the result
Array.prototype.concat.apply(
// transform from parent => [parent, child1, child2 etc..]
[], originalContacts.map(contact => [contact, ...contact.children])
);
let filteredContacts = contactsAndSubContacts.filter((item)=>{
// NOTE: you probably want to use toLowerCase()
// on this.state.search as well
return item.name.toLowerCase().indexOf(this.state.search.toLowerCase())!==-1;
});
return(<div>
<h1> Contact List </h1>
<input type="text"
value={this.state.search}
placeholder="Search Here"
onChange={this.updateSearch.bind(this)} />
<ul>
{
filteredContacts.map((obj, i) =>
<Tab value={obj.name} key={i} />)
}
</ul>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Can't focus on React input

Having some trouble trying to focus in on an element. I have a mapped array function that spits out html with inputs. It is possible to have multiple id's, so I want to set the ref to be 'type' + Id. The two possible types are task and subtask. When I try access via this.refs.{refValue}.focus() I get a Cannot read property 'focus' of undefined
Here's my jsx:
<input className="ui-input-text" type="text" ref="subTask + {subTask.Id}" onChange={this.handleSubTaskChange.bind(this, indx, idx)} value={subTask.Name} />
Here's where I get my error
var subTaskRef = 'subTask' + subTaskId;
this.refs.subTaskRef.focus();
The variable subTaskId is correct, I have verified that. Perhaps I am setting the ref incorrectly?
EDIT
After following #Ori Drori's answer, here's some more code:
class Tasks extends React.Component {
focusTasks: [],
focusSubTasks: [],
constructor(props) {
super(props);
this.state = {
editableTasks: [],
editableSubTasks: [],
tasks: [],
subTasks: [],
plannerId: this.props.plannerId,
};
var state = this.state;
}
and (part) of my render method
render() {
const tasks = this.state.tasks.map((task, idx) => {
var editable = this.state.editableTasks.filter(id => id === task.Id).length > 0;
var editableSubTasks = this.state.editableSubTasks;
const subTaskComponents = task.SubTasks.map((subTask, indx) =>
<li key={subTask.Id} className="list-group-item" style={{minHeight: '50px', border: 0, backgroundColor: 'rgba(127,191,63,.42)'}}>
<div className="pull-left" style={{width: '50%'}}>
<!-- Pay attention to this line -->{editableSubTasks.filter(id => id === subTask.Id).length > 0 ? <input className="ui-input-text" type="text" ref={ (ref) => this.focusSubTasks[subTask.Id] = ref } onChange={this.handleSubTaskChange.bind(this, indx, idx)} value={subTask.Name} /> : <span>{subTask.Name}</span>}
</div>
<div className="pull-right" style={{marginTop: '-5px', width: '50%'}}>
<div className="pull-right">
<button className="btn btn-default" onClick={() => { this.EditSubTask(task.Id, subTask.Id)}}>{editableSubTasks.filter(id => id === subTask.Id).length > 0 ? <i className="fa fa-check"></i> : <i className="fa fa-pencil-square-o"></i>}</button>
</div>
</div>
</li>
);
Here's where the issue seems to be (won't build)
Ended up just using jQuery, it's much easier when it's one line of code. Not sure if what I'm doing is too complicated for this, but I ended up setting an id on the inputs, and just calling $(el).focus() to solve this problem. Unless someone has a working example, I will update this SO.
Using the ref callback just to set a property on the class is a common pattern for accessing DOM elements and React creators recommend to use this pattern instead of this.refs.myRef pattern.
class MyComponent extends React.Component {
// ..
render() {
return (
<input ref={(thisEl) => { this['name' /* + subTask.Id */] = thisEl }} />
)
}
}
Now you can just use it as this['name' /* + subTask.Id */].focus().
However, Im not 100% sure if that could be the cause of your issue, especially because you didn't let us know if console.log(this.refs) actually has correct elements and if you didn't make mistakes.
Let me know how it works out for you.
I don't recommend to use jQuery, in other words: don't mix avoidable imperative code with declarative code. It seems like an easy solution for your issues but if you'll get the whole point of React, you'll understand that jQuery is not the easy solution, especially in long run.

Resources