Reactjs looping through object with map - reactjs

I'm not sure what kind of problem this is, but I have a state.sports which contains some data about sports. I added new data to this object and then I tried to render it on the DOM with this component.
class AddProject extends Component {
constructor() {
super();
this.state = {
newSport:{}
}
this.handleSubmit = this.handleSubmit.bind(this);
}
static defaultProps = {
Types:['air','aquatic','land']
}
handleSubmit(e){
if (!this.refs.sport.value) {
alert("title required");
} else {
this.setState({
newSport:{
sport: this.refs.sport.value,
type: this.refs.type.value
}
},function() {
this.props.addSport(this.state.newSport);
});
}
e.preventDefault();
}
render() {
let typeOptions = this.props.Types.map(type => {
return <option key={type} value={type}>{type}</option>
});
return (
<div className="addproject">
<h3>Add project </h3>
<form onSubmit={this.handleSubmit}>
<div>
<label>Sport </label><br/>
<input type="text"
key="sport"
ref="sport"
placeholder="Add project" />
</div>
<div>
<label>Type</label><br/>
<select ref='type'>
{typeOptions}
</select>
</div>
<input type='submit' value='Submit' />
</form>
</div>
);
}
}
export default AddProject;
Then I added this formatted data into a list with this component:
class Project extends Component {
render() {
let Sports;
//check if there is data in props.sports which is bind to state.sports
if (this.props.sports) {
Sports = this.props.sports.map(sport => {
return (
//after some expirements i found that the key is the cause of the problem
<ProjectItems key={sport.name} sport={sport} />
);
});
}
return (
<div className="project">
<h1>List </h1>
<ul>
{Sports}
</ul>
</div>
);
}
}
Then ProjectItems formats the data into name of the sport and type of sport. Anyway when I enter some data it only renders the type of sport and the sport's name returns undefined, like here. Can you tell me what's wrong ?

It would be more clear if you gave code of ProjectItems class, but at first glance it looks like in handleSubmit method you sholud change this:
newSport:{
sport: this.refs.sport.value,
type: this.refs.type.value
}
to this:
newSport:{
name: this.refs.sport.value,
type: this.refs.type.value
}

maybe what you want to achieve is set a nested object properties on the state.
use this:
this.setState(
{newSport:
update(this.state.newSport,
{type: {$set: this.refs.type.value}},
{name: {$set: this.refs.sport.value}}
)
});
the sports JSON structure will be:
newSport: {type: "air", name: "land"}
the
then you can display the sports:
this.props.sports.map(sport => {
return (
//the key will not be a problem, since now it has values in it.
<ProjectItems key={sport.name} sport={sport} />
);

Related

Editable textbox in react js

class myInfo extends Component {
constructor(props) {
super(props);
this.state = {
name: sessionStorage.getItem('name'),
};
}
componentDidMount() {
axios.post('http://localhost:8080/allProfiles', {
"name": this.state.name
})
.then((response) => {
document.getElementById("email").innerHTML = "email: " + response.data.email;
})
.catch(function (error) {
console.log(error);
});
}
render() {
return (
<div>
<p id="email"></p>
</div>
);
}
}
Noob practising react here. Given the name my rest api would just return a json will all of its profile stuff like
{
"email": ...
"description: ...
}
For simplicity it will just get the email for now. The output of this page at the moment is
email: userFromThisSession#gmail.com
All in text. My goal is
image
Without the fancy css^^
So its the username for above image but same concept for email. I want
email: a textbox (has the email from above) (and then a button to edit it)
Anyone know how? I've been searching for a while but no luck
example
class Info extends Component {
constructor(props) {
super(props);
this.state = {
name: ""
};
this.handleChange = this.handleChange.bind(this);
this.updateIsEdit = this.updateIsEdit.bind(this);
}
handleChange(e) {
this.setState({
name:e.target.value
})
}
updateIsEdit(e, value="null") {
this.setState({
isEdit: value
});
}
render() {
return (
<div>
<input type="text" name="userName" value={this.state.name} placeholder="Enter your name..." onChange={this.handleChange} onBlur={this.updateIsEdit}/>
<img src="https://img.icons8.com/android/24/000000/edit.png" className="edit" />
<p id="name">{this.state.name}</p>
</div>
);
}
}
render(<Info />, document.getElementById('root'));
What I would do is make a component that is a simple <span> that becomes a textbox when you click on the icon:
/// inside your render() method:
(this.state.editMode)
? <input className='edit-email' type='text' value={ this.state.email || '' } onBlur={this.toggleEditEmail} />
: <>
<span className='edit-email' >{ this.state.email }</span>
<img href="edit.png" onClick={this.toggleEditEmail} />
</>
/// Toggle function:
toggleEditEmail() {
this.setState({ editMode: !this.state.editMode });
}
You would have to then match the style of the <span> and <input> so that the input doesn't show the usual border, width, etc.
Also, note that you shouldn't need to set the HTML node directly:
document.getElementById("email").innerHTML = "email: " + response.data.email;
The React way of doing it is by setting the state:
this.setState({ email: response.data.email });
The render() I posted above will then use the email coming from this.state.

Input tag: nothing typed is appearing

My first React session data storage, so thanks. I am trying to set up inputing data and then placing it in session storage, so it can be edited, viewed or deleted later. There are 2 pieces of data, "title" and "note" to be inputed into a form. Nothing happens when I type into the form inputs. Any other help welcome also.
class AddNote extends Component {
constructor(props) {
super(props);
this.state = {
title: '',
content: ''
}
}
componentDidMount() {
this.getFormData();
}
//let notes = getSessionItem(keys.notes);
//if (!notes) { notes = ""; }
onTitleChange(event) {
this.setState({ title: event.target.value }, this.storeFormData);
this.storeFormData();
}
onContentChange(event) {
this.setState({ content: event.target.value }, this.storeFormData);
}
storeFormData() {
const form = {
title: this.state.title,
content: this.state.content
}
setSessionItem(keys.user_form, form);
}
getFormData() {
const form = getSessionItem(keys.user_form);
if (form) {
this.setState({
title: form.name,
content: form.content
});
}
}
render() {
return (
<div>
<div>
<h2>ADD NOTE PAGE</h2>
</div>
<form classname="nav1">
<div>
<label><b>Title</b></label>
<input type="text"
value={this.state.title}
onchange={this.onTitleChange.bind(this)}
/>
</div>
<div>
<label><b>Content</b></label>
<input type="text"
value={this.state.content}
onchange={this.onContentChange.bind(this)}
/>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default AddNote;
and the storage file:
export const keys = {
title: 'title',
notes: 'notes'
}
export const getSessionItem = function (key) {
let item = sessionStorage.getItem(key);
item = JSON.parse(item);
return item;
}
export const setSessionItem = function (key, value) {
value = JSON.stringify(value);
sessionStorage.setItem(key, value);
}
export const removeSessionItem = function (key) {
sessionStorage.removeItem(key);
}
No need to have 2 change handler for your input. You can do it using a common change handler.
<form classname="nav1">
<div>
<label><b>Title</b></label>
<input type="text"
value={this.state.title}
name="title" <---- Provide name here
onChange={this.onChange}
/>
</div>
<div>
<label><b>Content</b></label>
<input type="text"
value={this.state.content}
name="content" <---- Provide name here
onChange={this.onChange}
/>
</div>
<button type="submit">Submit</button>
</form>
Your onChange function should be, and use callback in setState to call your storeFormData function.
onChange = (e) => {
this.setState({
[e.target.name] : e.target.value
}, () => this.storeFormData())
}
Note: In React we use camelCase, for example, onchange should be onChange and classname should be className.
Also make sure you bind this to storeFormData and getFormData functions, or you can use arrow function's to automatically bind this.
Demo

Push an item in array using ReactJS

class Demo extends React.Component{
constructor (){
super();
this.state = {
list : ['car','map', 'house']
}
}
inputValue(e){
var x = e.target.value;
console.log(x)
}
addValue(){
this.state.list.push();
this.setState({list: this.state.list});
}
render(){
return(
<div>
<input onChange={this.inputValue} type="text"/>
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
)
}
}
ReactDOM.render(
<Demo/>,
document.getElementById('test')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="test"></div>
Using my code, how can i push the value from <input onChange={this.inputValue} type="text"/> in list : ['car','map', 'house']. I use for this addValue function, but i can't insert the x variable from inputValue function in push() from addValue function. How to do this using my code?
You need a state value for the text-input so that your addValue() function knows what to use when its time to add a new item. The text state will be updated with anything the user types.
Working demo: https://codesandbox.io/s/magical-feynman-fze1n
import React from "react";
class Demo extends React.Component {
constructor() {
super();
this.state = {
text: "",
list: ["car", "map", "house"]
};
}
inputValue(e) {
this.setState({
text: e.target.value
});
}
addValue() {
const text = this.state.text;
this.setState({ list: [...this.state.list, text] });
}
render() {
return (
<div>
<input onChange={this.inputValue.bind(this)} type="text" />
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
);
}
}
export default Demo;
Also, refrain from doing direct state-mutations like this.state.list.push(blah). This is against React principles and can lead to unwanted visual side-effects. If you need to reference an existing state, try to create a copy of it instead. In the case for you list, we use the spread-operator to create a shallow-copy and then added the new item to the array..
Since React is all about small components and reusability consider breaking it up into two separate components... That way, if you need a form anywhere else you can reuse it...
Here is your Demo:
class Demo extends Component {
state = { list: ['car', 'map', 'house'] };
addItem = item => {
this.setState({ list: [item, ...this.state.list] });
};
render() {
return (
<div>
<Form addItem={this.addItem} />
{this.state.list.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}
}
And here is the Form:
class Form extends Component {
state = { item: '' };
handleChange = event => {
this.setState({ item: event.target.value });
};
handleSubmit = event => {
event.preventDefault();
this.props.addItem(this.state.item);
this.setState({ item: '' });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
value={this.state.item}
onChange={this.handleChange}
/>
</form>
);
}
}
Live Demo: https://stackblitz.com/edit/react-611uzp

ReactJS/Javascript/Graphql/React-apollo: passing query variables

I am trying to figure out how to pass the input variables to a graphql query function to run a query and display the results. Not sure if I am passing the variables correct when the button is clicked. The getObjectQuery takes two variables startTime and endTime, both will be selected on the frontend by the user.
Parent Component:
class Calendar extends React.Component {
constructor(props) {
super(props);
this.state = {
startTime: '',//This will keep track of the time
endTime:'',
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit = event => {
event.preventDefault();
console.log(this.state.startTime);
this.setState({
startTime: new Date(document.getElementById("startTime").value).valueOf(),//getElementById is a jQuery method
endTime: new Date(document.getElementById("endTime").value).valueOf()
}, () => {
this.props.data.refetch({//Assign the inputvalues, which is the current state, to the variables after pressing the submit button
startTime: this.state.startTime,
endTime:this.state.endTime
});
console.log(this.state.startTime);
console.log(this.state.endTime);
});
};
render() {
console.log(this.props.data);//This is where data is.
return (
<div className="Calendar">
<form onSubmit={this.handleSubmit.bind(this)}>
<label>Start Time</label>
<input type="datetime-local" id="startTime" step="1" />
<label>End Time</label>
<input type="datetime-local" id="endTime" step="1" onSubmit={this.handleSubmit.bind(this)}/>
<input type="submit" value="Submit" />
</form>
{<ElementList startTime={this.state.startTime} endTime={this.state.endTime}/>}
</div>
);
}
};
export default graphql(getObjectsQuery,
{ options: (ownProps) => {
console.log(ownProps.startTime);
return ({ variables: { startTime: ownProps.startTime,
endTime: ownProps.endTime
} })
} } )(Calendar);
Child Function
const ElementList = (props) => (
<Query
query={getObjectsQuery}
variables={props.startTime, props.endTime}
>
{({ loading, error, data }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error</p>;
return (
<Item.Group divided>
{data.action.map(action =>
<div>
<ul>
<li>{action.action}</li>
<li>{action.timestamp}</li>
<ul>
{action.object.map( (obj) => {
return (<li>{obj.filename}</li>)
})}
</ul>
</ul>
</div>
)}
</Item.Group>
);
}}
</Query>
);
export default ElementList;
I believe your problem may be that you're just passing your props in as variables. They need to be set to specific property names in order for your graphql resolvers to accept them.
variables={{start: props.startTime, end: props.endTime}}

React updating props with state data

I have a Component that is handling a contact forum submission from a user. I want to take the state that the user submits and add it to my props data. Right now everything is working, but the handleSubmit, I am not sure how to take the state and pass it to my this.data.props to update the data to include the new object.
My data is an array of Objects. The state takes user input and updates itself. Next I want to take the state object and add it to my props.data and then display it on the screen.
EDIT: UPDATED WITH LATEST CODE
import React, { Component, PropTypes } from 'react';
const testData = [
{
name: 'Joe',
email: 'joemail'
},
{
name: 'Bill',
email: 'billmail'
},
{
name: 'Dude',
email: 'dudemail'
}
]
class FormContact extends Component {
constructor(props) {
super(props)
this.state = {
formValues: {
name: '',
email: ''
}
}
}
handleChange(event) {
let formValues = this.state.formValues;
let name = event.target.name;
let value = event.target.value;
formValues[name] = value;
this.setState({
formValues
});
}
handleSubmit(event) {
event.preventDefault();
console.log("NEW FORM VALUES " + this.state.formValues.name + " " + this.state.formValues.email);
const {name, email} = this.state.formValues
this.props.addContact({name, email});
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<label> Name:
<input type="text" name="name" placeholder="Name" value={this.state.formValues["name"]} onChange={this.handleChange.bind(this)} />
</label><br />
<label> Email:
<input type="text" name="email" placeholder="Email" value={this.state.formValues["email"]} onChange={this.handleChange.bind(this)}/>
</label><br />
<input className="btn btn-primary" type="submit" value="Submit" />
</form>
)
}
}
FormContact.PropTypes = {
data: PropTypes.arrayOf(
PropTypes.object
)
}
FormContact.defaultProps = {
data: testData
}
class Contact extends Component {
constructor(props) {
super(props)
this.state = {
data: testData
}
}
addContact(contact) {
this.setState({data: this.state.data.concat(contact)});
}
render() {
const renObjData = this.props.data.map( (anObjectMapped, index) => {
return (<p key={index}>
Name: {anObjectMapped.name} < br/>
Email: {anObjectMapped.email} <br /></p>
)
});
return (
<div>
<h1>CONTACT PAGE</h1>
<FormContact data={this.state.data} addContact={this.addContact.bind(this)} />
{renObjData}
</div>
)
}
}
Contact.PropTypes = {
data: PropTypes.arrayOf(
PropTypes.object
)
}
Contact.defaultProps = {
data: testData
}
export default Contact;
What you are looking at here is having a parent container that passes down data as props to the form component. You already have your Contact component so you can make it hold the data state.
How it would work is you would write a function on the Contact component called addContact and it would take a contact as an argument and then set its own state with the new contact IE concat it to its own data array through setting state.
class Contact extends React.Component {
constructor() {
super();
this.state = {
data: testData
}
}
addContact = (contact) => {
this.setState({data: this.state.data.concat(contact)});
};
render() {
const contacts = _.map(this.state.data, (value, index) => {
return <li key={index + value}> {value.email} {value.name} </li>
})
return (
<div>
<h1>CONTACT PAGE</h1>
<FormContact data={this.state.data} addContact={this.addContact} />
<h3> Contacts</h3>
<ul>{contacts} </ul>
</div>
)
}
}
and then in your handleSubmit function all you have to do is add
handleSubmit(event) {
event.preventDefault();
const {name, email} = this.state.formValues
this.props.addContact({name, email})
}
this will push it onto the data array in the parent component and then once the parent component updates it will pass that down as props to the form component.
Here is a code pen showing all that in action. http://codepen.io/finalfreq/pen/VKPXoN
UPDATE: Also in Contacts added how to display data, you can easily replace lodash _.map with this.state.data.map(function(value, index)

Resources