Take input value from input and add to state - reactjs

I just started learning reactjs, and i can't do something simple as taking a value from an input and add to an existing variable like when im using basic javascript DOM. I tried using DOM but i guess it doesn't really works on reactjs.
I want to make a button that replaces value of 'name' property individually inside of state with input[type='text'] value, which in this case there are 3 inputs for each property.
how do i do that?
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
state = {
data :[
{Name: 'rommy',
Ages: 24,
Occupation: 'coder'
},
{Name: 'andi',
Ages: 43,
Occupation: 'Teacher'
},
{Name: 'susilo',
Ages: 42,
Occupation: 'Mobile Dev'
}
]
}
renderBiodata = () => {
let {data} = this.state;
return data.map((val) =>{
return(
<div style={{border: '1px solid black'}}>
<div>
{val.Name}
</div>
<div>
{val.Ages}
</div>
<div>
{val.Occupation}
</div>
<div>
<input type="text"/>
</div>
<div>
<input type="button" value="Change Name" onClick="changeName"/>
</div>
<div></div>
<div></div>
</div>
)
})
}
render() {
return (
<div style={{display:'flex', justifyContent: 'center'}}>
{this.renderBiodata()}
</div>
)
}
}
export default App;

I created an example of how to update the state from an input.
https://codesandbox.io/s/gallant-cohen-lv8p0?fontsize=14&hidenavigation=1&theme=dark
I prefer the functional component way but also created a class component with the same behavior.
Let me know if you need more clarity.

You are using an Array of Object concept over here. Changing values in an Array of Object is bit complex. If you understand it well then it's good otherwise I will suggest you to read about it. :)
Suggestion - How to manage React State with Arrays
Code below is solution to your problem.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
state = {
data: [
{ Name: "rommy", Ages: 24, Occupation: "coder", inputVal: "" },
{ Name: "andi", Ages: 43, Occupation: "Teacher", inputVal: "" },
{ Name: "susilo", Ages: 42, Occupation: "Mobile Dev", inputVal: "" }
]
};
renderBiodata = () => {
let { data } = this.state;
return data.map((val, i) => {
return (
<div style={{ border: "1px solid black" }}>
<div>{val.Name}</div>
<div>{val.Ages}</div>
<div>{val.Occupation}</div>
<div>
<input
type="text"
value={val.inputVal}
onChange={e => {
let _val = Object.assign({}, val);
_val.inputVal = e.target.value;
this.setState((state, prop) => ({
data: state.data.map((x, j) => {
if (i === j) return _val;
return x;
})
}));
}}
/>
</div>
<div>
<input
type="button"
value="Change Name"
onClick={() => {
let _val = Object.assign({}, val);
_val.Name = val.inputVal;
this.setState((state, prop) => ({
data: state.data.map((x, j) => {
if (i === j) return _val;
return x;
})
}));
}}
/>
</div>
<div />
<div />
</div>
);
});
};
render() {
return (
<div style={{ display: "flex", justifyContent: "center" }}>
{this.renderBiodata()}
</div>
);
}
}
export default App;

Related

Unable to pass props from parent to child and save it in state of child component

I'm trying to develop a website for fetching GitHub data, but I'm having problem in updating the component that shows data Formdata component. It doesn't seem to be updating form some reasons.
App:
export default class App extends Component {
constructor(props){
super(props);
this.state = {
uname:'',
udata:'',
};
this.handleInput = this.handleInput.bind(this);
this.getUser = this.getUser.bind(this);
}
getUser(){
fetch(`https://api.github.com/users/${this.state.uname}`)
.then(response => response.json())
.then(data => this.setState({udata:data}))
.catch(error => console.error(error));
}
handleInput(event){
this.setState({
uname:event.target.value
});
}
render() {
return (
<div>
<Header></Header>
<Form handleInput={this.handleInput} uname={this.state.uname} getUser={this.getUser}></Form>
<Formdata udata={this.state.udata}></Formdata>
</div>
)
}
}
Form:
export default function Form(props) {
const {getUser, handleInput, uname} = props;
return (
<div className="form">
<input className="textbar" placeholder="Search for username" value={uname} onChange={handleInput} name="uname"></input>
<button className="button" onClick={getUser} >Search</button>
</div>
)
}
Formdata:
export default class Formdata extends Component {
constructor(props){
super(props);
this.state = {
follower:'',
following:'',
public_repos:'',
visit_page:'',
avatar:''
}
this.updateUser = this.updateUser.bind(this);
};
componentDidMount(props){
this.updateUser();
}
updateUser(){
this.setState({follower:this.props.udata.followers});
this.setState({following:this.props.udata.following});
this.setState({public_repos:this.props.udata.public_repos});
this.setState({visit_page:this.props.udata.url});
this.setState({avatar:this.props.udata.avatar_url});
console.log(this.props.udata);
}
render() {
return (
<div>
<img className="imge" src= {this.state.avatar} alt=" "></img>
<div className="details">
<div className="compon">Followers: {this.state.followers}</div>
<div className="compon">Following: {this.state.following}</div>
<div className="compon">public repos" {this.state.public_repos}</div>
</div>
<div className="urls">Page:{this.state.visit_page}</div>
</div>
)
}
}
I can't figure out how to update component Formdata on clicking search button in Form component.
Full Working App: StackBlitz
import React, { Component, useEffect } from "react";
import "./style.css";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
uname: "",
udata: ""
};
this.handleInput = this.handleInput.bind(this);
this.getUser = this.getUser.bind(this);
}
getUser() {
fetch(`https://api.github.com/users/${this.state.uname}`)
.then(response => response.json())
.then(data =>
this.setState({ udata: data }, () => {
console.log(this.state.udata);
})
)
.catch(error => console.error(error));
}
handleInput(event) {
this.setState(
{
uname: event.target.value
},
() => {
console.log(this.state.uname);
}
);
}
render() {
return (
<div>
<Form
handleInput={this.handleInput}
uname={this.state.uname}
getUser={this.getUser}
/>
<Formdata udata={this.state.udata} />
</div>
);
}
}
const Form = props => {
const { getUser, handleInput, uname } = props;
return (
<div className="form">
<input
className="textbar"
placeholder="Search for username"
value={uname}
onChange={handleInput}
name="uname"
/>
<button className="button" onClick={getUser}>
Search
</button>
</div>
);
};
const Formdata = ({ udata }) => {
useEffect(() => {
console.log(JSON.stringify(udata.login));
}, [udata]);
return (
<div style={styles.card}>
{udata.login ? (
<div style={styles.cardImg}>
<div>
<img
style={styles.img}
className="imge"
src={udata?.avatar_url}
alt=" "
/>
</div>
<div className="details">
<div className="compon">Followers: {udata?.followers}</div>
<div className="compon">Following: {udata?.following}</div>
<div className="compon">Public repos: {udata?.public_repos}</div>
<div className="urls">Page: {udata?.url}</div>
</div>
</div>
) : (
<div>
<p>No Data Available</p>
</div>
)}
</div>
);
};
const styles = {
card: {
display: "flex",
flex: 1,
backgroundColor: "rgba(21,21,21,0.2)",
padding: 10,
marginTop: 10,
borderRadius: 5
},
cardImg: {
display: "flex",
flex: 1,
flexDirection: "row",
flexWrap: "wrap",
overflow: "hidden",
textOverflow: "ellipsis",
color: "rgba(0,0,0,0.7)"
},
img: {
marginRight: 10,
width: 100,
height: 100,
borderRadius: 10,
overflow: "hidden"
}
};
Do not copy props into state, use the props directly in your JSX:
div>
<img className="imge" src= {this.props.udata.avatar} alt=" "></img>
<div className="details">
<div className="compon">Followers: {this.props.udata.followers}</div>
<div className="compon">Following: {this.props.udata.following}</div>
<div className="compon">public repos" {this.props.udata.public_repos}</div>
</div>
<div className="urls">Page:{this.props.udata.visit_page}</div>
</div>
If you copy props into state, you are creating redundant copy of props and it is difficult to keep props and state in sync. And it is a React anti-pattern.
Just make sure this.props.udata is not undefined, it is ok if it is empty object {}. If it is undefined, put a check / conditional rendering.
anti-pattern-unconditionally-copying-props-to-state
Formdata.updateUser() isn't being called at any point. You probably just need to call it in componentDidMount():
export default class Formdata extends Component {
...
componentDidMount(props){
this.updateUser();
}
updateUser(){
this.setState({follower:this.props.udata.followers});
this.setState({following:this.props.udata.following});
this.setState({public_repos:this.props.udata.public_repos});
this.setState({visit_page:this.props.udata.url});
this.setState({avatar:this.props.udata.avatar_url});
console.log(this.props.udata);
}
...
}

style react component on click

so i have this simple divs of names:
i just want to press on one of them and get a background color of green and when pressing on another one the first one will be canceled so just one will be colored at a time. what i simply need is inline style or i don't know i'm stuck.
first.js:
import React from 'react';
function SidebarComponents({name,title,selected,onSelect}) {
const style={
cursor: "pointer"
};
const classes = {
selected: {
backgroundColor: '#00ff00'
}
}
return (
<div
name={name}
title = {title}
style={style}
>
{name}
</div>
)
}
export default SidebarComponents;
second.js:
import React, { useEffect, useState } from "react";
import SidebarComponents from "../SidebarComponents/SidebarComponents";
import 'bootstrap/dist/css/bootstrap.min.css';
import '../Sidebar1/Sidebar.css';
function Sidebar({ onChange }) {
const [selectedComponent, setSelectedComponent] = useState({
componentsName: [
{ name: "John Smith", title: "John Smith" },
{ name: "Male, 26 years old", title: "Gender and age" },
{ name: "john", title: "Alerts" },
{ name: "claude", title: "Recent" },
{ name: "edward", title: "Blood pressure" },
{ name: "mira", title: "Body weight" },
{ name: "alex", title: "Glucose" },
{ name: "zac", title: "SPO2" }
]
});
return (
<div>
{selectedComponent.componentsName.map(component => {
return (
<div className="row align-items-start sidebar-components">
<div className="col">
<SidebarComponents
name={component.name}
title={component.title}
/>
</div>
</div>
);
})}
</div>
);
}
export default Sidebar;
on Sidebar:
const [selectedName, setSelectedName] = useState(null);
//...
<SidebarComponents
name={component.name}
title={component.title}
selected={component.name === selectedName}
onSelect={setSelectedName}
/>
on SidebarComponents:
const selectedClassName = selected ? 'selected' : '';
//...
<div
name={name}
title={title}
style={style}
className={`sidebar ${selectedClassName}`} //you must add sidebar and selected classes to your styles
onClick={() => onSelect(name)}
>
{name}
</div>
Add key attribute to div, inside the map.
Handel onClick event, to store the selected element index/value in your state.
Apply style using conditional rendering of className.
second.js
<div>
{selectedComponent.componentsName.map((component, index) => {
return (
<div key={index} onClick={() => handelOnClick(index)} className="row align-items-start sidebar-components">
<div className="col">
<SidebarComponents
name={component.name}
title={component.title}
className={selectedIndex === index ? 'highlight' : ''}
/>
</div>
</div>
);
})}
</div>
As you are rendering text in first.js no need to use div wrapper, use 'p', 'span' tag
In second.js instead of iterating entire div block, use ul li

Objects are not valid as a React child (found: object with keys {children}): ReactJS application

I am not sure why am getting this error: Objects are not valid as a React child (found: object with keys {children}): ReactJS.
Below is my child component.
import React, { Component } from 'react';
import $ from "jquery";
import "../Dropdown/Dropdown.css"
class Dropdown extends Component {
constructor(props) {
super(props);
this.state = {
listOpen: false,
headerTitle: "-"
};
this.close = this.close.bind(this);
}
componentDidUpdate() {
const { listOpen } = this.state;
setTimeout(() => {
if (listOpen) {
window.addEventListener("click", this.close);
} else {
window.removeEventListener("click", this.close);
}
}, 0);
}
componentWillUnmount() {
window.removeEventListener("click", this.close);
}
close(timeOut) {
this.setState({
listOpen: false
});
}
selectItem(title, id, stateKey) {
console.log("ko" + id + stateKey + title);
this.setState(
{
headerTitle: title,
listOpen: false
},
this.props.resetThenSet(id, stateKey, title)
);
}
toggleList = () => {
console.log("hiiii");
this.setState(prevState => ({
listOpen: !prevState.listOpen
}));
}
handleSelect(ranges) {
console.log(ranges[0]._d.toISOString().slice(0, 10));
console.log(ranges[1]._d.toISOString().slice(0, 10));
}
render() {
const { list } = this.props;
const { listOpen, headerTitle } = this.state;
$(".example").click(function () {
$("#examples").hide();
});
return (
<div className="dd-wrapper">
<div className="dd-header" onClick={this.toggleList}>
<div className="dd-header-title">
{this.props.title}{" "}
<div style={{ color: "#00bdf2" }}>
{headerTitle === "-" ? "" : headerTitle}
</div>{" "}
</div>
{listOpen ? (
{/* <FontAwesome name="angle-up" size="1g" /> */ }
) : (
{/* <FontAwesome name="angle-down" size="1g" /> */ }
)}
</div>
{listOpen && (
<ul className="dd-list" onClick={e => e.stopPropagation()}>
{list.map(item => (
<li
className="dd-list-item"
key={item.id}
onClick={() => this.selectItem(item.title, item.id, item.key)}
>
{item.title} {item.selected}
{/* && <FontAwesome name="check" /> */}
}
</li>
))}
<hr style={{ borderColor: "#4e4949" }} />
<span className="dd-list-item"> Custom Range</span>
</ul>
)}
</div>
);
}
}
export default Dropdown;
Parent component:
class DefectsContainer extends Component {
state = {
apicategory: [{
id: 0,
title: 'Advisor Ballot',
selected: false,
key: 'apicategory'
}, {
id: 1,
title: 'Advisor IDs',
selected: false,
key: 'apicategory'
}, {
id: 2,
title: 'Advisor Meeting',
selected: false,
key: 'apicategory'
}]
};
render() {
return (
<div className="defect-filters">
<Dropdown title="API Name" list={this.state.apicategory}> </Dropdown>
</div>
)
}
}
Below is the error am getting:
Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
in div (at Dropdown.js:69)
in div (at Dropdown.js:68)
in Dropdown (at DefectsContainer.js:55)
in div (at DefectsContainer.js:54)
in div (at DefectsContainer.js:49)
in div (at DefectsContainer.js:48)
in div (at DefectsContainer.js:47)
in DefectsContainer (created by Context.Consumer)
I tried to figure out, but couldn't do it. I am new to react. Can someone please help where am going wrong. Thanks in advance!

Toggle class only on one element, react js

I`m changing class after clicking and it works.
The problem is that, classes change simultaneously in both elements and not in each one separately. Maybe someone could look what I'm doing wrong. Any help will be useful.
import React, { Component } from "react";
class PageContentSupportFaq extends Component {
constructor(props) {
super(props);
this.state = {
isExpanded: false
};
}
handleToggle(e) {
this.setState({
isExpanded: !this.state.isExpanded
});
}
render() {
const { isExpanded } = this.state;
return (
<div className="section__support--faq section__full--gray position-relative">
<div className="container section__faq">
<p className="p--thin text-left">FAQ</p>
<h2 className="section__faq--title overflow-hidden pb-4">Title</h2>
<p className="mb-5">Subtitle</p>
<div className="faq__columns">
<div
onClick={e => this.handleToggle(e)}
className={isExpanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>First</strong>
</p>
</div>
<div
onClick={e => this.handleToggle(e)}
className={isExpanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>Second</strong>
</p>
</div>
</div>
</div>
</div>
);
}
}
export default PageContentSupportFaq;
Every element must have its seperate expanded value. So we need an array in state.
And here is the code:
import React, { Component } from "react";
class PageContentSupportFaq extends Component {
state = {
items: [
{ id: 1, name: "First", expanded: false },
{ id: 2, name: "Second", expanded: true },
{ id: 3, name: "Third", expanded: false }
]
};
handleToggle = id => {
const updatedItems = this.state.items.map(item => {
if (item.id === id) {
return {
...item,
expanded: !item.expanded
};
} else {
return item;
}
});
this.setState({
items: updatedItems
});
};
render() {
return this.state.items.map(el => (
<div
key={el.id}
onClick={() => this.handleToggle(el.id)}
className={el.expanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>{el.name}</strong>
<span> {el.expanded.toString()}</span>
</p>
</div>
));
}
}
export default PageContentSupportFaq;
You can get two state one state for first and another for a second and handle using two function like this
import React, { Component } from 'react';
class PageContentSupportFaq extends Component {
constructor(props) {
super(props)
this.state = {
isExpanded: false,
isExpanded2:false,
}
}
handleToggle(e){
this.setState({
isExpanded: !this.state.isExpanded
})
}
handleToggle2(e){
this.setState({
isExpanded2: !this.state.isExpanded2
})
}
render() {
const {isExpanded,isExpanded2} = this.state;
return (
<div className="section__support--faq section__full--gray position-relative">
<div className="container section__faq">
<p className="p--thin text-left">FAQ</p>
<h2 className="section__faq--title overflow-hidden pb-4">Title</h2>
<p className="mb-5">Subtitle</p>
<div className="faq__columns">
<div onClick={(e) => this.handleToggle(e)} className={isExpanded ? "active" : "dummy-class"}>
<p className="mb-0"><strong>First</strong></p>
</div>
<div onClick={(e) => this.handleToggle2(e)} className={isExpanded2 ? "active" : "dummy-class"}>
<p className="mb-0"><strong>Second</strong></p>
</div>
</div>
</div>
</div>
);
}
}
export default PageContentSupportFaq;
You'll need to track toggled classes in array, that way it will support arbitrary number of components:
// Save elements data into array for easier rendering
const elements = [{ id: 1, name: "First" }, { id: 2, name: "Second" }];
class PageContentSupportFaq extends Component {
constructor(props) {
super(props);
this.state = {
expanded: []
};
}
handleToggle(id) {
this.setState(state => {
if (state.isExpanded.includes(id)) {
return state.isExpanded.filter(elId => elId !== id);
}
return [...state.expanded, id];
});
}
render() {
return elements.map(el => (
<div
key={el.id}
onClick={() => this.handleToggle(el.id)}
className={this.isExpanded(el.id) ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>{el.name}</strong>
</p>
</div>
));
}
}

React app not showing in Codepen no matter what?

I have a react app that I made in VS Studio, putting it into codepen, it doesnt seem to load a thing, any suggestions?
I have tried making sure React is linked and checked all of my syntax, no errors on local host but no display in codepen.
I have looked through the code multiple times and I feel its such a silly mistake
https://codepen.io/donnieberry97/pen/EzmOvW
class TodoListt extends React.Component {
state = {};
constructor(props) {
super(props);
this.state = {
userInput: "",
list: [],
editing: false,
};
}
changeUserInput(input) {
this.setState({
userInput: input
})
}
addToList() {
if (this.state.userInput === "") { (alert("Please enter a To-do")); return; };
const { list, userInput } = this.state;
this.setState({
list: [...list, {
text: userInput, key: Date.now(), done: false
}],
userInput: ''
})
}
handleChecked(e, index) {
console.log(e.target.checked);
const list = [...this.state.list];
list[index] = { ...list[index] };
list[index].done = e.target.checked;
this.setState({
list
})
}
handleEditing(e) {
this.setState({
editing: true
})
}
handleRemoved(index) {
const list = [...this.state.list];
list.splice(index, 1);
this.setState({
list
})
}
render() {
var viewStyle = {};
var editStyle = {};
if (this.state.editing) {
viewStyle.display = "none"
}
else {
editStyle.display = "none"
}
return (
<div className="to-do-list-main">
<input
onChange={(e) => this.changeUserInput(e.target.value)}
value={this.state.userInput}
type="text"
/>
<div class="submitButton">
<button onClick={() => { this.addToList(this.state.userInput) }}>Add todo</button>
</div>
{this.state.list.map((list, index) => (
<div className="form">
<ul>
{/* <div style={viewStyle} onDoubleClick={this.handleEditing.bind(t his)}> */}
<li key={list.key}>
<div class="liFlexCheck">
<input type="checkbox" onChange={(e) => this.handleChecked(e, index)} />
</div>
<div class="liFlexText">
<div class="liFlexTextContainer">
<span style={{ textDecoration: list.done ? 'line-through' : 'inherit' }}>
{list.text}
</span>
</div>
</div>
<button onClick={(index) => this.handleRemoved(index)}>Remove</button>
<input
type="text"
style={editStyle}
value={list.text}
/>
</li>
{/* </div> */}
</ul>
</div>
))}
</div>
);
}
}
Remove the import statements, working example.
You shouldn't use import when you got External Scripts.
Also, you got many errors in your code that should be handled, like:
<div class="submitButton">, use className.
Each child in a list should have a unique key prop.
Form field with value prop but without onChange handler.
Check out the logs:
In codpen, you don't need to import the react instead just write code,
here is codepen working one : codepen
from codesandbox, you can learn with all imports also because it doesn't uses any external scripts,
your code will work fine if you add an import to it
that is import ReactDOM from 'react-dom';
codesandbox will show all these suggestions,
here is codesandbox working example: codesandbox

Resources