Trouble iterating/mapping props - javascript

I'm trying to develop a simple application in which any number of tasks will be rendered as cards. I'm passing them as props, schemed like so:
taskList: [{
taskID: 1,
taskTitle: 'Task 1',
taskDescription: 'Description 1',
completed: true
}]
By logging the props in the TaskCard component, I can see the list arrives exactly like that. If I try and log something such as props[0].taskDescription, it'll successfully return "Description 1", like so:
export default function TaskCard(props) {
return(
<div className="task-card" draggable>
<h3> Test </h3>
{ props[0].taskDescription } // this actually works
</div>
)
}
I can't, however, map or calculate the length of the props to iterate through props.
What am I doing wrong in terms of iteration? Is it a flawed architecture in terms of componentization?

To render a list of TaskCards, you need to do the mapping of taskList outside that component like so:
{taskList.map(task => <TaskCard task={task} />)}
and then TaskCard would render using the passed task props:
TaskCard(props) {
// use props.task.taskDescription, etc.
}

First, thank you all who contributed with your insights in the comments for the original posting. So yes, apparently passing an array as a prop is troublesome. I may not have resolved in the most efficient way, but here's what it has come down to so far. Use the image in the original post to guide yourself.
I have resolved the issue the following way:
UserArea.jsx
Contains a state that logs the user, username, a list containing pending tasks pendingList and a list containing complete tasks completedList;
It renders a header, the PendingArea and CompleteArea component passing {...this.state} to each of them (the full component is an object).
render(){
return(
<div className="user-area-container">
<div className="profile-header">
<h2>{ this.state.userName }</h2>
{ this.state.email }
</div>
<PendingArea {...this.state} />
<CompletedArea {...this.state} />
</div>
)
}
PendingArea.jsx and CompleteArea.jsx
Here I made the filtering, passing only the equivalent mapping to each of the components. The code for the PendingArea component is as follows:
function PendingArea(props) {
var pendingTasks = props.pendingList.map(task => {
return <TaskCard {...task} />
});
return(
<div className="status-container">
<div className="status-title">
<h2>Pending tasks</h2>
</div>
<div className="status-modifier">
{ pendingTasks }
</div>
</div>
)
}
TaskCards.jsx
Finally, the TaskCard component uses the direct properties from the props:
export default function TaskCard(props) {
return(
<div className="task-card" draggable>
<h3> { props.taskTitle } </h3>
{ props.taskDescription }
</div>
)
}
This way I managed to properly render the task card in their due place. I hope this works for anyone reading this, as well.

Related

How to fix unique "key" prop and validateDOMNesting(...) in ReactJS with fuctional component

Hi all I have following code.
Test.js component
const Test = ({ infoText }) => {
return (
<div>
Some Text
{infoText && <p>{infoText}</p>}
</div>
);
};
export default Test;
App.js component
export default function App() {
return (
<div className="App">
<Test
infoText={[
<p className="someStyles">"Looking to lease multiple devices?"</p>,
<div className="someOtherStyles">
<b>Get in contact with our specialist.</b>
</div>,
]}
/>
</div>
);
}
When I am rendering my Test component in App.js file I am getting errors like
Warning: Each child in a list should have a unique "key" prop.
Warning: validateDOMNesting(...): <div> cannot appear as a descendant of <p>.
I know that is coming from this syntax that I wrote.
infoText={[
<p className="someStyles">"Looking to lease multiple devices?"</>,
<div className="someOtherStyles">
<b>Get in contact with our specialist.</b>
</div>,
]}
I need to write in this way because my Test component is reusable component and I am using it's infoText prop for passing various tags with specific classes.
By the way, the code works. But it's very ugly that I have so many errors in the console. Please help me fix this.
This warning is generated because usually, when a react element is an array, that array is generated dynamically, and so might change. In this scenario, you absolutely need the elements in your list to have a unique key prop to avoid unexpected behaviour from React.
In this scenario, you are absolutely fine to ignore the warnings, because your array is hardcoded, and is never going to change. If you don't want to see these warnings, you could change the array to be a react fragment, like this:
const Test = ({ infoText }) => {
return (
<div>
Some Text
{infoText && <p>{infoText}</p>}
</div>
);
};
<Test
infoText={
<>
<p className="someStyles">"Looking to lease multiple devices?"</p>
<div className="someOtherStyles">
<b>Get in contact with our specialist.</b>
</div>
</>
}
/>
A more idiomatic way of achieving the same thing might be to have your Test component render its children, like this:
const Test = ({ children }) => {
return (
<div>
Some Text
<p>{children}</p>
</div>
);
};
<Test>
<p className="someStyles">"Looking to lease multiple devices?"</p>
<div className="someOtherStyles">
<b>Get in contact with our specialist.</b>
</div>
</Test>

How to use props within the same component?

I am looking to make a component that is filled with tweets.
I am creating an array called "tweets" and I would like to access the array in the return(). I know about mapping, but is there a way to just display one on the page? I thought it would be like {this.props.name} or just {props.name} but it doesn't render anything to the page.
How can I reference the props to my page within the same component?
Tweets.js
import React from 'react';
const Tweets = (props) => {
const tweets = [
{name: 'Name 1',
tweet:'This is a tweet!'},
{name:'Name 2',
tweet:'This is another tweet'},
{name:'Name 3',
tweet:'This is yet another tweet!'},
]
return (
<section>
<h1>{this.props.name}</h1>
</section>
)
}
export default Tweets;
Your tweets are inside the component itself, they are not coming from props. So you can access a single value with something like this:
{tweets[0].name}
If you want to display them all, you can do something like this:
return (
<section>
{
tweets.map(({name, tweet}) => (
<div>
<h1>{name}</h1>
<p>{tweet}</p>
</div>
))
}
</section>
)

Passing props from Container with fetch to one element via Link

This is a bit hypotetical situation as i do not have live example. My question is:
I have some random data:
id
name
description
link
photo
github
short description
I have a Projects.js component where I am fetching data
render() {
const { projects } = this.state;
return (
<div className='Projects__Content'>
{projects .map(project => {
return (
<div key={project.id} className='Projects__Project'>
<h3>{project.name}</h3>
<p>{projects.shortDescription}</p>
<Link to=`/project/${project.id}`/>
</div>
);
})}
</div>
);
}
So here in my Projects.js I am rendering all projects that I have in my data, but only I am using some information. From my rendered projects with React Router Link, I want to go to specific project using ID. For that part I have another component Project.js.
Now in Project.js I want to use all data that I fetched in Projects.js, but part is that I am not getting right. Can I pass a props all my fetched data? During my search I found that I should not pass props with Link.
So can I do this like this:
render() {
const { projects } = this.state;
return (
<div className='Projects__Content'>
{projects .map(project => {
return (
<div key={project.id} className='Projects__Project'>
<h3>{project.name}</h3>
<p>{projects.shortDescription}</p>
<Link to=`/project/${project.id}`/>
</div>
);
})}
<Project dataProps={...projects} />
</div>
);
}
But this will render my one project from Project.js, so this is not what I want.
How about this:
render() {
const { projects } = this.state;
return (
<div className='Projects__Content'>
{projects .map(project => {
return (
<div key={project.id} className='Projects__Project'>
<h3>{project.name}</h3>
<p>{projects.shortDescription}</p>
<Link to=`/project/${project.id}` propsData={...projects}/>
</div>
);
})}
</div>
);
}
Or maybe I should use another fetch in Project.js
With redux I can use one reducer to do that, but how this should be handled with React only?
Thanks for help :)
Write Project component which will be used to show the Project details.
The part that is troubling you is that you should have some kind of routing to know which Project to show and you already have link defined, so just add a project route at /project/:projectId.
There are 3 ways that you can share your projects array with project:
Just pass the whole object along you already have in projects component down to project component
Use Redux of some state management to store it globally
Fetch project individually inside project component by project id

Why child component didnt get parent's props with Axios in React

I solve it with a simple flag like so:
I added a new flag property on the states object: loaded:false
I update the loaded value to true, when i get the data, like so:
helpers.getValues().then(results => this.setState({values:results.data,loaded:true}));
And finally, inside the render() i first check if the loaded==true and then i render the ChildComponent, like so:
{this.state.loaded == true ? <ChildComponent values={this.state.values} name="theodore"/> : ''}
I am making a simple code in React that gets data with Axios.
The data are returned to the client , so the communication is OK.
The problem is that I pass the results data to a child component , as props, and as soon as the child component loads it asks for the props.data but it is empty.
Below is the Parent component:
Inside the componentDidMount I call the axios to get the data and update the setState object.
Below, into the render function, I pass the results to the ChildComponent.
var ParentComponent = React.createClass({
getInitialState:function(){
return {
values:''
activeTab:0
}
},
componentDidMount:function(){
helpers.getValues().then(results => this.setState({values:results.data}));
},
render:function(){
return(
<div className='container'>
<div className="row">
<div className="col-sm-8 text-center" >
<h1>{pageTitle}</h1>
<Tabs activeKey={this.state.activeTab} onSelect={this.handleSelect} id="controlled-tab-example">
<Tab eventKey={tabs[0].key} title={tabs[0].name}>
Tab1
<ChildComponent1 values={this.state.values} test="ok"/>
</Tab>
<Tab eventKey={tabs[1].key} title={tabs[1].name}>Tab2</Tab>
</Tabs>
</div>
</div>
</div>
)
}
And here i show the ChildComponent(in a seperate js file).
Inside the componentDidMount I try to show the props but the object that gets it like:
{values:'',test:'ok'}
var ChildComponent = React.createClass({
componentDidMount:function(){
console.log(this.props);
},
render:function(){
return(
<div>
<ul>'nothing'</ul>
</div>
)
}
});
I guess that it is a delay issue, in which the chld component loads before the axios async returns the data from server
Any help from someone that has dealt with a similar situation would be appriciated, thanks.
Because you're passing this.state.movies, instead of this.state.values to the ChildComponent.
How you do it:
<ChildComponent1 values={this.state.movies} test="ok"/>
How it should be:
<ChildComponent1 values={this.state.values} test="ok"/>

Update functionality in react

I am newbie to React and I am trying to do update on react. I don't get the exact logic to make it and hence I need your help.
On click of update, I managed to get the values of selected contact but later on, i don't get how to populate those value onto input text boxes and again on submit after change of values, update the selected contact. I came across onChange but I don't understand.
Clues i knew:
this.refs.name.value and this.refs.number.value are values which are in input textbox . And on update, we need to set these value into the state on that corresponding index.
My code and screenshot is below:
Person.js - number is taken as the key , considering individual number is unique
editcontact(id){
this.props.onChange(id);
}
render() {
return(
<div className="panel panel-default">
<div className="panel-heading">
<h4>{this.props.detail.name} </h4>
<a className="b" href="#" onClick={this.deletecontact.bind(this,this.props.detail.number)}> Delete </a>
<a className="b" href="#" onClick={this.editcontact.bind(this,this.props.detail.number)}> Update </a>
</div>
<h6 className="panel-body">{this.props.detail.number}</h6>
</div>
</div>
)
}
It is passed to Contact.js
editcontact(id)
{
this.props.onChange(id);
}
render() {
var details;
if(this.props.data){
details=this.props.data.map(dts=>{
return(
<Person key={dts.number} detail={dts} onChange={this.editcontact.bind(this)} onDelete={this.deletecontact.bind(this)}></Person>
)
})
}
Then comes App.js
handleEdit(id){
console.log(id);
let cts=this.state.contacts;
let index=cts.findIndex( x => x.number === id);
console.log(cts[index]);
this.setState({ selectedContact: cts[index]; });
}
render() {
return (
<div className="App">
<div className="page-header">
<h2>Contact list</h2>
</div>
<AddContact newOne={this.state.selectedContact} addcontact={this.handleAddition.bind(this)}></AddContact>
<Contact onChange={this.handleEdit.bind(this)} onDelete={this.handleDelete.bind(this)} data={this.state.contacts}> </Contact>
</div>
);
}
AddContact.js
constructor(){
super();
this.state={
newContact:{
name:'',
number:''
}
}
}
addcontact(e){
// console.log(this.refs.name.value);\
e.preventDefault();
this.setState({
newContact:{
name: this.refs.name.value,
number:this.refs.number.value
}
},function(){
console.log(this.state.newContact);
this.props.addcontact(this.state.newContact);
})
this.refs.name.value="";
this.refs.number.value="";
}
render() {
console.log(this.props.newOne);
return (
<div className="col-md-6">
<form onSubmit={this.addcontact.bind(this)}>
<div className="form-group">
<label>Name </label>
<input className="form-control" type="text" ref="name" />
</div>
<div className="form-group">
<label>Number</label>
<input className="form-control" type="number" ref="number" />
</div>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
what you need is to tell your component that you have a new state and you want it to re-render.
handleEdit(id){
console.log(id);
let cts=this.state.contacts;
let index=cts.findIndex( x => x.number === id);
this.setState({ selectedContact: cts[index]; });
}
render() {
return (
<div className="App">
<div className="page-header">
<h2>Contact list</h2>
</div>
<AddContact addcontact={this.handleAddition.bind(this)}></AddContact>
<Contact onChange={this.handleEdit.bind(this)} onDelete={this.handleDelete.bind(this)} data={this.state.contacts}> </Contact>
</div>
);
with the setState function you updating the state of this compoent and also make it to re-render. now you can decide what you want to do with this data: this.state.selectedContact like passing it to AddContact
Don't use .bind(this... there is no reason to do it. Use attribute={() => this.functionName()}
Don't use different naming, use some pattern for attributes names. e.g. addcontact should be addContact
Don't use so long lines. Use Eslint to show you all of such tips.
It's really hard to read your code now, make it more readable and you will have better time editing it yourself.
And now to have update, i would suggest using pure functional component to display things and higher order component to manage state of data.
(props) => <form> .... <input value={props.email} /> ... </form;
and in parent component, which is responsible for all data management add state. In state you can save values and pass it into child components using props.
When you will advance in React, you will start using extra libraries to manage state of the app, e.g. Redux. It makes similar thing, but all app's state is in one place and then you can access it from any part of the app. E.g. you show these inputs, then jump to another state of app to add some other thing and then you can easily jump back to this state and still have input's values that are partly entered.
Just save values in state. No matter how you manage your app's state and push values to display components using props. Google, read, check some videos on Youtube and you will get it.
https://facebook.github.io/react/docs/thinking-in-react.html

Categories

Resources