Why event.target.value is undefined? - javascript

I have two components named parent and child.
Child component contains checkbox's and it will send selected checkbox's values to Parent component
Child
class Child extends Component{
state = {
options: [],
selected_options: []
}
handleClose = (e) => {
this.props.add(this.state.selected_options)
}
handleChange = (event) => {
console.log(event.target.name);
if(event.target.checked==true){
event.target.checked=false
this.state.selected_options.slice(this.state.options.indexOf(event.target.value),1)
}
else{
event.target.checked = true
this.state.selected_options.push(event.target.value)
}
}
render() {
return(
<div>
<Grid>
{
this.state.options.map(value => {
return(
<Checkbox onChange={this.handleChange} label={value.name} value={value.name} checked={false} />
)
})
}
<Button color='green' onClick={this.handleClose} inverted>
<Icon name='checkmark' /> Apply
</Button>
</div>
);
}
}
and Parent Component
class Parent extends Component {
constructor(props){
super(props);
this.state = {
selected_options:[],
}
}
addOptions = (options) => {
this.setState({
selected_options: options
})
}
render() {
return(
<div>
<Child selected_options={this.state.selected_options} add={this.addOptions} />
</div>
);
}
}
When a checkbox is selected it must output its corresponding value in the console. but it showing undefined

Directly mutating the state or the event value is not the correct idea
You should be doing it like
class Child extends Component{
state = {
checkedState: []
options: [],
selected_options: []
}
handleClose = (e) => {
this.props.add(this.state.selected_options)
}
handleChange = (index, value) => {
var checkedState = [...this.state.checkedState];
if(checkedState[index] === undefined) {
checkedState.push(true)
}
else {
if(checkedState[index] === true) {
var selected_options=[...this.state.selected_options];
var idx = selected_options.findIndex((obj) => obj.name == value)
selected_options.splice(idx, 1);
checkedState[index] = false
} else {
var selected_options=[...this.state.selected_options];
selected_options.push(value);
}
}
this.setState({checkedState, selected_options})
}
render() {
return(
<div>
<Grid>
{
this.state.options.map((value, index) => {
return(
<Checkbox onChange={() => this.handleChange(index, value.name)} label={value.name} value={value.name} checked={this.state.checkedState[index] || false} />
)
})
}
<Button color='green' onClick={this.handleClose} inverted>
<Icon name='checkmark' /> Apply
</Button>
</div>
);
}
}
and from the Parent render the child and not the Parent
Parent
render() {
return(
<div>
<Child selected_options={this.state.selected_options} add={this.addOptions} />
</div>
);
}

Related

Unable to render a child component inside main Component using iteration

I have a main component defined as App.js
import "./styles.css";
import { Component } from "react";
import Item from "./components/Item";
class App extends Component {
constructor(props) {
super(props);
this.state = { textInput: "", items: [] };
}
insertItem() {
if (this.state.textInput !== "") {
this.setState((state) => {
const list = state.items.push(state.textInput);
return {
items: list,
textInput: ""
};
});
}
}
deleteItem(index) {
this.setState((state) => {
const list = [...state.items];
list.splice(index, 1);
return {
items: list,
textInput: ""
};
});
}
handleChange(event) {
this.setState({ textInput: event.target.value });
}
render() {
const template = (
<div>
<div>
<input
type="text"
value={this.state.textInput}
onChange={(e) => this.handleChange(e)}
/>
<button onClick={this.insertItem.bind(this)}>Add</button>
</div>
<div>
{this.state.items.map((item, idx) => {
return <Item name={item} removeItem={this.deleteItem.bind(this, idx)} />;
})}
</div>
</div>
);
return template;
}
}
export default App;
and a child Component defined in Item.js
import { Component } from "react";
class Item extends Component {
render() {
return (
<div>
<span>{this.props.name}</span>
<span onClick={this.props.removeItem}>X</span>
</div>
);
}
}
export default Item;
Now my UI looks like
In the above code(App.js) Iam trying to iterate the items and then display the names using the child component. But due to some reason, on entering the text in the input and clicking add its not showing up. Also there are no errors in the console.
Please help, thanks in advance
Edited:
After the recent changes I get this error
You do not need to call this.deleteItem(idx) while passing it to the child.
import "./styles.css";
import { Component } from "react";
import Item from "./components/Item";
class App extends Component {
constructor(props) {
super(props);
this.state = { textInput: "", items: [] };
}
insertItem() {
if (this.state.textInput !== "") {
this.setState((state) => {
const list = state.items.push(state.textInput);
return {
items: list,
textInput: ""
};
});
}
}
deleteItem(index) {
this.setState((state) => {
const list = state.items.splice(index, 1);
return {
items: list,
textInput: ""
};
});
}
handleChange(event) {
this.setState({ textInput: event.target.value });
}
render() {
const template = (
<div>
<div>
<input
type="text"
value={this.state.textInput}
onChange={(e) => this.handleChange(e)}
/>
<button onClick={this.insertItem.bind(this)}>Add</button>
</div>
<div>
{this.state.items.map((item, idx) => {
return <Item name={item} removeItem={this.deleteItem.bind(this, idx)} />;
})}
</div>
</div>
);
return template;
}
}
export default App;
Updating state in react requires a new reference to objects.You're using Array#push. It will not detect your new change and the DOM will not update. You need to return a new reference.
insertItem() {
if (this.textInput === "") {
this.setState((state) => {
// const list = state.items.push(state.textInput);
const list = [...state.items, state.textInput];
return {
list,
textInput: ""
};
});
}
}
In order to track the array, you must add the key attribute:
{this.state.items.map((item, idx) => {
return <Item key={idx} name={item} removeItem={this.deleteItem(idx)} />;
})}
Here I used the index, but it would be better to use some ID of your model.
UPDATE:
I'd move the handler binding in the constructor:
constructor(props) {
super(props);
this.state = { textInput: "", items: [] };
this.insertItem = this.insertItem.bind(this);
}
Then:
<button onClick={this.insertItem}>Add</button>
UPDATE 2:
Okay, it seems you have several mistakes (and I didn't notice them at first glance).
Here is the complete working source (tested):
class App extends Component {
constructor(props) {
super(props);
this.state = { textInput: "", items: [] };
}
insertItem() {
if (this.state.textInput !== "") {
this.setState((state) => {
//const list = state.items.push(state.textInput);
const list = [...state.items, state.textInput];
return {
items: list,
textInput: ""
};
});
}
}
deleteItem(index) {
this.setState((state) => {
const list = [...state.items];
list.splice(index, 1);
return {
items: list,
textInput: ""
};
});
}
handleChange(event) {
this.setState({ textInput: event.target.value });
}
render() {
const template = (
<div>
<div>
<input
type="text"
value={this.state.textInput}
onChange={(e) => this.handleChange(e)}
/>
<button onClick={this.insertItem.bind(this)}>Add</button>
</div>
<div>
{this.state.items.map((item, idx) => {
return <Item key={idx} name={item} removeItem={this.deleteItem.bind(this, idx)} />;
})}
</div>
</div>
);
return template;
}
}
export default App;
I think you should move display of template into return statement rather than inside render
...
render() {
return (
<div>
<div>
<input
type="text"
value={this.state.textInput}
onChange={(e) => this.handleChange(e)}
/>
<button onClick={this.insertItem.bind(this)}>Add</button>
</div>
<div>
{this.state.items.map((item, idx) => {
return <Item name={item} removeItem={this.deleteItem.bind(this, idx)} />;
})}
</div>
</div>
);
}

Function is not being called as it should in React onClick()

Basically, i created a button to call a function when the button is clicked, it's working, the console.log() shows the message "Working", but the script inside it, it's not being shown on the browser. I have no idea what's wrong, could someone help me? I'm very new in Software Engineer and started learning React a few days ago, so i dont know too much about it.
This is the entire code:
class QuizGame extends React.Component {
constructor() {
super();
this.state = {
questions: Questions
}
}
Game() {
const { questions } = this.state;
const item = questions[Math.floor(Math.random() * questions.length)];
const items = [item.a, item.b, item.c, item.d];
console.log('Working');
return (
<div>
<GameParagraph value={item.ques} />
{
items.map(quest => (
<div key={quest}>
<div>
<GameButton
value={quest}
onClick={() => {
if(item.ans === quest) {
return console.log('Working');
} else {
return console.log('not working')
}
}}
/>
</div>
</div>
))
}
</div>
)
}
render() {
return (
<StartGame
onClick={() => this.Game()}
/>
);
}
}
And that's the button to call the Game():
class StartGame extends React.Component {
render() {
return (
<button onClick={this.props.onClick}>Start</button>
);
}
}
You can try like this:
class QuizGame extends React.Component {
constructor() {
super();
this.state = {
questions: Questions,
hasGameStarted: false
}
}
Game() {
const { questions } = this.state;
const item = questions[Math.floor(Math.random() * questions.length)];
const items = [item.a, item.b, item.c, item.d];
console.log('Working');
return (
<div>
<GameParagraph value={item.ques} />
{
items.map(quest => (
<div key={quest}>
<div>
<GameButton
value={quest}
onClick={() => {
if(item.ans === quest) {
return console.log('Working');
} else {
return console.log('not working')
}
}}
/>
</div>
</div>
))
}
</div>
)
}
startGameClicked() {
this.setState({hasGameStarted: true});
}
render() {
return (
<StartGame
onClick={this.startGameClicked}
/>
{this.state.hasGameStarted ? this.Game() : null}
);
}
Make sure you are binding the onClick event in StartGame component properly.
You need to refactor your code. Returning JSX from an event handler (which is what your function Game is) won't cause your component to render.
Instead, the "React" way to handle this is to provide some state variable that can be updated by the super class to cause a render. i.e.
class QuizGame extends React.Component {
constructor() {
super();
this.state = {
started: false,
questions: Questions
}
}
startGame() { // JavaScript functions should be camelCase
this.setState({started: true});
}
render() {
if (this.state.started) {
return const { questions } = this.state;
const item = questions[Math.floor(Math.random() * questions.length)];
const items = [item.a, item.b, item.c, item.d];
return (
<div>
<GameParagraph value={item.ques} />
{items.map(quest => (
<div key={quest}>
<div>
<GameButton
value={quest}
onClick={() => {
if(item.ans === quest) {
return console.log('Working');
} else {
return console.log('not working')
}
}}/>
</div>
</div>
))}
</div>);
} else {
return <StartGame onClick={() => this.startGame()} />;
}
}
}

After the submit is clicked, the input is not submitted in React js

My code has 2 classes which are "Forum" and PostEditor". When the button "Post" is clicked which is in the "PostEditor" class, the textarea which has the state "newPostBody" is submitted but the input which has the state "newTitle" is not submitted in ReactJs. Below there is an image of it. What am I doing wrong?
const Post = props => (
<div>
<div >{props.postBody}</div>
</div>
);
class PostEditor extends Component {
constructor(props) {
super(props);
this.state = {
newPostBody: '',
newTitle: ''
};
}
handleInputChange(ev) {
this.setState({
newPostBody: ev.target.value
});
}
handleTitleChange(ev) {
this.setState({
newTitle: ev.target.value
});
}
createPost() {
this.props.addPost(this.state.newPostBody, this.state.newTitle);
this.setState({
newPostBody: '',
newTitle: ''
});
}
render() {
return (
<div>
<div>
<input type="text" value={this.state.newTitle}
onChange={this.handleTitleChange.bind(this)}/>
<textarea value={this.state.newPostBody}
onChange={this.handleInputChange.bind(this)} />
<button onClick={this.createPost.bind(this)}
disabled={!this.state.newPostBody} > Post </button>
</div>
</div>
);
}
}
class Forum extends Component {
constructor(props) {
super(props);
this.state = {
posts: [],
};
}
addPost(newPostBody) {
const newState = Object.assign({}, this.state);
newState.posts.push(newPostBody);
this.setState(newState);
}
render() {
return (
<div>
{this.state.posts.map((postBody, idx) => {
return (
<div >
<Post key={idx} postBody={postBody} />
</div>);
})}
<PostEditor addPost={this.addPost.bind(this)} />
</div>
);
}
}
Your addPost method in Forum class is only expecting body and not expecting 2nd argument which I guess would be title:
You should update it with something like below:
class Forum extends Component {
constructor(props) {
super(props);
this.state = {
posts: [],
};
}
addPost(newPostBody, newPostTitle) {
const newState = Object.assign({}, this.state);
let post = {body: newPostBody, title: newPostTitle}
newState.posts.push(post);
this.setState(newState);
}
render() {
return (
<div>
{this.state.posts.map((post, idx) => {
return (
<div >
<Post key={idx} postBody={post.body} postTitle={post.title} />
</div>);
})}
<PostEditor addPost={this.addPost.bind(this)} />
</div>
);
}
}
Post.js
const Post = props => (
<div>
<div> {props.postTitle} </div>
<div> {props.postBody} </div>
</div>
);
it looks like in your addPost function after clicking the Post button, you are not receiving the same params as the arguments you provided from the createPost:
createPost() {
this.props.addPost(this.state.newPostBody, this.state.newTitle); <== 2 args
this.setState({
newPostBody: '',
newTitle: ''
});
}
vs
addPost(newPostBody) { <== 1 arg only
const newState = Object.assign({}, this.state);
newState.posts.push(newPostBody);
this.setState(newState);
}
I think you want to make the call to addPost look like: this.props.addPost({body: this.state.newPostBody, title: this.state.newTitle})
Then you will have to update the Post UI to handle both title and body values.

Show and Hide specific component in React from a loop

I have a button for each div. And when I press on it, it has to show the div with the same key, and hide the others.
What is the best way to do it ? This is my code
class Main extends Component {
constructor(props) {
super(props);
this.state = {
messages: [
{ message: "message1", key: "1" },
{ message: "message2", key: "2" }
]
};
}
handleClick(message) {
//something to show the specific component and hide the others
}
render() {
let messageNodes = this.state.messages.map(message => {
return (
<Button key={message.key} onClick={e => this.handleClick(message)}>
{message.message}
</Button>
)
});
let messageNodes2 = this.state.messages.map(message => {
return <div key={message.key}>
<p>{message.message}</p>
</div>
});
return <div>
<div>{messageNodes}</div>
<div>{messageNodes2}</div>
</div>
}
}
import React from "react";
import { render } from "react-dom";
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
messages: [
{ message: "message1", id: "1" },
{ message: "message2", id: "2" }
],
openedMessage: false
};
}
handleClick(id) {
const currentmessage = this.state.messages.filter(item => item.id === id);
this.setState({ openedMessage: currentmessage });
}
render() {
let messageNodes = this.state.messages.map(message => {
return (
<button key={message.id} onClick={e => this.handleClick(message.id)}>
{message.message}
</button>
);
});
let messageNodes2 = this.state.messages.map(message => {
return (
<div key={message.key}>
<p>{message.message}</p>
</div>
);
});
const { openedMessage } = this.state;
console.log(openedMessage);
return (
<div>
{openedMessage ? (
<div>
{openedMessage.map(item => (
<div>
{" "}
{item.id} {item.message}{" "}
</div>
))}
</div>
) : (
<div> Not Opened</div>
)}
{!openedMessage && messageNodes}
</div>
);
}
}
render(<Main />, document.getElementById("root"));
The main concept here is this following line of code.
handleClick(id) {
const currentmessage = this.state.messages.filter(item => item.id === id);
this.setState({ openedMessage: currentmessage });
}`
When we map our messageNodes we pass down the messages id. When a message is clicked the id of that message is passed to the handleClick and we filter all the messages that do not contain the id of the clicked message. Then if there is an openedMessage in state we render the message, but at the same time we stop rendering the message nodes, with this logic {!openedMessage && messageNodes}
Something like this. You should keep in state only message key of visible component and in render method you should render only visible component based on the key preserved in state. Since you have array of message objects in state, use it to render only button that matches the key.
class Main extends Component {
constructor(props) {
super(props);
this.state = {
//My array messages: [],
visibleComponentKey: '',
showAll: true
};
handleClick(message) {
//something to show the specific component and hide the others
// preserve in state visible component
this.setState({visibleComponentKey : message.key, showAll: false});
};
render() {
const {visibleComponentKey, showAll} = this.state;
return (
<div>
{!! visibleComponentKey && ! showAll &&
this.state.messages.filter(message => {
return message.key == visibleComponentKey ? <Button onClick={e => this.handleClick(message)}>{message.message}</Button>
) : <div /> })
}
{ !! showAll &&
this.state.messages.map(message => <Button key={message.key} onClick={e => this.handleClick(message)}>{message.message}</Button>)
}
</div>
);
}
}
I haven't tried it but it gives you a basic idea.
I cannot reply to #Omar directly but let me tell you, this is the best code explanation for what i was looking for! Thank you!
Also, to close, I added a handleClose function that set the state back to false. Worked like a charm!
onCloseItem =(event) => {
event.preventDefault();
this.setState({
openedItem: false
});
}

Remove class name sibling component when click the current component and make toggling in current to remove class it self

i'm new in react and try to make a button that will remove the sibling class if i click in the current button, and also the current button can remove it self class (toggling),i already try and it seems im not find the clue. here my code below .
export default class Child extends Component {
render(){
return(
<button
onClick={this.props.onClick}
className={this.props.activeMode ? 'active' : ''}>
{this.props.text}
</button>
)
}
}
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
selectedIndex: null,
};
}
handleClick (selectedIndex) {
this.setState({
selectedIndex,
});
}
render () {
const array = ["Button 1","Button 2"];
return (
<div>
{array.map((obj, index) => {
const active = this.state.selectedIndex === index;
return <Child
text={obj}
activeMode={active}
key={index}
onClick={() => this.handleClick(index)} />
})}
</div>
)
}
}
I'm not shure that I understood your clearly right
Did you meen something like this?
class Child extends React.Component {
render(){
return(
<button
onClick={this.props.onClick}
className={this.props.activeMode ? 'active' : ''}>
{this.props.text}
</button>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedIndex: null,
};
}
handleClick (selectedIndex) {
let selected = this.state.selectedIndex === selectedIndex ? null : selectedIndex;
this.setState({
selectedIndex: selected,
});
}
render () {
const array = ["Button 1","Button 2"];
return (
<div>
{
array.map((obj, index) => {
return <Child text={obj}
activeMode={this.state.selectedIndex === index}
key={index}
onClick={() => this.handleClick(index)} />
})
}
</div>
)
}
}
ReactDOM.render(
<Parent name="World" />,
document.getElementById('container')
);
.active {
color: green;
}
<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="container">
<!-- This element's contents will be replaced with your component. -->
</div>

Categories

Resources