for the life of me, i do not know why my react app isn't working. My input state get's rendered after onSubmit. but my value isn't disappearing in my input bar. and nothing is being render on my list. I've only had this problem after breaking my components to smaller chunks, i had no problem making this work when it was 1 whole component.
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
items: []
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
input: e.target.value
})
}
handleSubmit(e) {
e.preventDefault();
this.setState({
input: '',
items: [...this.state.items,this.state.input]
})
console.log(this.state.input)
}
render() {
return (
<div>
<Form handleChange={this.handleChange} handleSubmit={this.handleSubmit}
value={this.state.input}/>
<List items={this.state.items}/>
</div>
)
}
}
class Form extends React.Component {
constructor(props){
super(props)
}
render() {
return (
<form onSubmit={this.props.handleSubmit}>
<input onChange={this.props.handleChange} value={this.props.input}/>
<button>Submit</button>
</form>
)
}
}
class List extends React.Component {
render() {
return (
<ul>
{
this.props.items.map((item,index) => {
<li key={index}>{item}</li>
})
}
</ul>
)
}
}
"Not disappearing" problem is because of that:
<input onChange={this.props.handleChange} value={this.props.input}/>
There isn't any input prop, it is value. See:
value={this.state.input}
So it should be:
<input onChange={this.props.handleChange} value={this.props.value}/>
Listing problem is because of this:
{
this.props.items.map((item,index) => {
<li key={index}>{item}</li>
})
}
Here, you are using an arrow function and a body block. If you use a body block you have to use a return statement.
{
this.props.items.map( ( item, index ) => {
return ( <li key={index}>{item}</li> );
} )
}
or you can use it like this:
{
this.props.items.map( ( item, index ) =>
<li key={index}>{item}</li> )
}
Related
I'm trying to send a form-submission handler to a child element through props. Everything renders, but when I click the submit button, I get no alert (see alert('Hi') in the handleSubmit function), and I also don't see the elements of SearchResults change. Instead, the whole page reloads and I'm back in the initial state. What is wrong?
Searcher.js:
class Searcher extends React.Component {
constructor(props) {
super(props);
this.state = {
results: [
{name:'1', key:0},
{name:'2', key:1}
]
};
}
handleSubmit = (event) => {
alert('Hi');
this.setState({
results: [
{name: 'hi', key: 0},
{name: 'again', key: 1}
]
})
event.preventDefault();
}
render() {
return (
<div>
<SearchForm/>
<SearchResults results={this.state.results} handleSubmit={this.handleSubmit}/>
</div>
)
}
}
export default Searcher;
SearchForm.js:
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) { this.setState({value: event.target.value}); }
render() {
return (
<form onSubmit={this.props.handleSubmit}>
<input type="text" value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
);
}
}
export default SearchForm;
SearchResults.js:
class SearchResults extends React.Component {
render() {
return (
this.props.results.map((result) => (<div key={result.key}>{result.name}</div>))
)
}
}
export default SearchResults;
Passing handeSubmit as props in <SearchForm /> in Searcher.js
render() {
return (
<div>
<SearchForm handleSubmit={this.handleSubmit}/> // Pass Handle Submit
<SearchResults results={this.state.results} /> // I don't see handleSubmit being used in SearchResults
</div>
)
}
Try passing handleSubmit to SearchForm or its value will be undefined
class Searcher extends React.Component {
...
handleSubmit = (event) => {
alert('Hi');
...
}
render() {
return <SearchForm handleSubmit={this.handleSubmit}/>
}
}
export default Searcher;
This may seem kind of basic but I'm just learning how to use React. Currently what I have going is when I type in the input field and submit, the system console logs my 'search' input. What I'm trying to do is pass my 'search' data from my child component to the parent. Looking for any tips or leads to the right direction.
This is what I have for my child component:
export default class SearchBar extends React.Component {
constructor(props) {
super(props);
this.state = {
search: ''
};
}
onChange = event => {
this.setState({ search: event.target.value });
};
onSubmit = event => {
const { search } = this.state;
event.preventDefault();
console.log(search);
};
render() {
return (
<div className='search-bar'>
<form onSubmit={this.onSubmit}>
<input
className='search'
type='text'
placeholder='Search'
onChange={this.onChange}
search={this.props.search}
value={this.state.searchinput}
parentCallback={this.onChange}
></input>
</form>
<FontAwesomeIcon className='search-icon' icon={faSearch} />
</div>
);
}
}
And in my Parent component (nothing much at the moment)
export default class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
search: ''
};
}
searchUpdate = search => {
console.log(search);
};
render() {
console.log(this.props.search);
return (
<div className='container'>
<SearchBar/>
</div>
);
}
}
Generally to pass data from child component to Parent Component, you can pass a reference of a function as props to child component from parent component and call that passed function from child component with data.
You can do something like this:
export default class SearchBar extends React.Component {
constructor(props) {
super(props);
this.state = {
search: ''
};
}
onChange = event => {
this.setState({ search: event.target.value });
};
onSubmit = event => {
const { search } = this.state;
event.preventDefault();
console.log(search);
this.props.passSearchData(search);
};
render() {
return (
<div className='search-bar'>
<form onSubmit={this.onSubmit}>
<input
className='search'
type='text'
placeholder='Search'
onChange={this.onChange}
search={this.props.search}
value={this.state.searchinput}
parentCallback={this.onChange}
></input>
</form>
<FontAwesomeIcon className='search-icon' icon={faSearch} />
</div>
);
}
In parent component:
export default class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
search: ''
};
}
searchUpdate = search => {
console.log(search);
this.setState({ ...state, search: search })
};
render() {
console.log(this.props.search);
return (
<div className='container'>
<SearchBar passSearchData={this.searchUpdate} />
</div>
);
}
The simplest way would be to pass a function from parent to child:
// in parent component
const setSearchValue = (search) => {
// setState to search
this.setState({search});
}
render (){
return <>
<SearchBar onsearch={this.setSearchValue} />
</>
}
// in child component
// change your searchUpdate
searchUpdate = () => {
const {onsearch} = this.state;
// function call to pass data to parent
this.props.onsearch(onsearch)
}
Just have a function that is passed as a prop to the child component. Let child component do the handle change part and pass the value back to the parent and then do whatever you want to with the value
Code sandbox: https://codesandbox.io/s/react-basic-example-vj3vl
Parent
import React from "react";
import Search from "./Search";
export default class Parent extends React.Component {
searchUpdate = search => {
console.log("in parent", search);
};
render() {
console.log(this.props.search);
return (
<div className="container">
<Search handleSearch={this.searchUpdate} />
</div>
);
}
}
Child
import React from "react";
export default class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
search: ""
};
}
onChange = event => {
this.setState({ search: event.target.value }, () => {
console.log("in child", this.state.search);
this.props.handleSearch(this.state.search);
});
};
onSubmit = event => {
const { search } = this.state;
event.preventDefault();
console.log(search);
};
render() {
return (
<div className="search-bar">
<form onSubmit={this.onSubmit}>
<input
className="search"
type="text"
placeholder="Search"
onChange={this.onChange}
search={this.props.search}
value={this.state.searchinput}
/>
</form>
</div>
);
}
}
I have have code that creates <li> elements. I need to delete elements one by one by clicking. For each element I have Delete button. I understand that I need some function to delete items by id. How to do this function to delete elements in ReactJS?
My code:
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {items: [], text: ''};
}
render() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
handleChange(e) {
this.setState({text: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
var newItem = {
text: this.props.w +''+this.props.t,
id: Date.now()
};
this.setState((prevState) => ({
items: prevState.items.concat(newItem),
text: ''
}));
}
delete(id){ // How that function knows id of item that need to delete and how to delete item?
this.setState(this.item.id)
}
}
class TodoList extends React.Component {
render() {
return (
<ul>
{this.props.items.map(item => (
<li key={item.id}>{item.text}<button onClick={this.delete.bind(this)}>Delete</button></li>
))}
</ul>
);
}
}
You are managing the data in Parent component and rendering the UI in Child component, so to delete item from child component you need to pass a function along with data, call that function from child and pass any unique identifier of list item, inside parent component delete the item using that unique identifier.
Step1: Pass a function from parent component along with data, like this:
<TodoList items={this.state.items} _handleDelete={this.delete.bind(this)}/>
Step2: Define delete function in parent component like this:
delete(id){
this.setState(prevState => ({
data: prevState.data.filter(el => el != id )
}));
}
Step3: Call that function from child component using this.props._handleDelete():
class TodoList extends React.Component {
_handleDelete(id){
this.props._handleDelete(id);
}
render() {
return (
<ul>
{this.props.items.map(item => (
<li key={item.id}>{item.text}<button onClick={this._handleDelete.bind(this, item.id)}>Delete</button></li>
))}
</ul>
);
}
}
Check this working example:
class App extends React.Component{
constructor(){
super();
this.state = {
data: [1,2,3,4,5]
}
this.delete = this.delete.bind(this);
}
delete(id){
this.setState(prevState => ({
data: prevState.data.filter(el => el != id )
}));
}
render(){
return(
<Child delete={this.delete} data={this.state.data}/>
);
}
}
class Child extends React.Component{
delete(id){
this.props.delete(id);
}
render(){
return(
<div>
{
this.props.data.map(el=>
<p onClick={this.delete.bind(this, el)}>{el}</p>
)
}
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<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='app'/>
I'm trying to call a simple method from the grandparent component in my child component but from some reason I can't , I tried every possible way but I think I'm missing something
here's the full code :
import React, { Component } from 'react';
import './App.css';
var todos = [
{
title: "Example2",
completed: true
}
]
const TodoItem = (props) => {
return (
<li
className={props.completed ? "completed" : "uncompleted"}
key={props.index} onClick={props.handleChangeStatus}
>
{props.title}
</li>
);
}
class TodoList extends Component {
constructor(props) {
super(props);
}
render () {
return (
<ul>
{this.props.todosItems.map((item , index) => (
<TodoItem key={index} {...item} {...this.props} handleChangeStatus={this.props.handleChangeStatus} />
))}
</ul>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos ,
text :""
}
this.handleTextChange = this.handleTextChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChangeStatus = this.handleChangeStatus(this);
}
handleTextChange(e) {
this.setState({
text: e.target.value
});
}
handleChangeStatus(){
console.log("hello");
}
handleSubmit(e) {
e.preventDefault();
const newItem = {
title : this.state.text ,
completed : false
}
this.setState((prevState) => ({
todos : prevState.todos.concat(newItem),
text : ""
}))
}
render() {
return (
<div className="App">
<h1>Todos </h1>
<div>
<form onSubmit={this.handleSubmit}>
< input type="text" onChange={this.handleTextChange} value={this.state.text}/>
</form>
</div>
<div>
<TodoList handleChangeStatus={this.handleChangeStatus} todosItems={this.state.todos} />
</div>
<button type="button">asdsadas</button>
</div>
);
}
}
export default App;
The method im trying to use is handleChangeStatus() from the App component in the TodoItem component
Thank you all for your help
This line is wrong:
this.handleChangeStatus = this.handleChangeStatus(this);
//Change to this and it works
this.handleChangeStatus = this.handleChangeStatus.bind(this);
I am trying to pass data from my search bar component which returns a JSON from an API call to another component that will display the information. I am having a lot of trouble figuring it out. I tried making a container that holds both of the components and pass the data. Any help would be appreciated.
Thanks!
My Search Bar Component
class SearchBar extends Component {
constructor(props){
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e){
e.preventDefault();
let reqBody = {
name: this.refs.name.value
};
fetch("/api", {
method: "POST",
body: JSON.stringify(reqBody),
headers: {"Content-Type": "application/json"}
})
.then((res) => {
if (res.ok){
return res.json();
} else {
throw new Error ('Something went wrong');
}
}).then((json) => {
console.log(json)
this.props.onDataFetched(json)
})
}
render() {
return (
<div >
<form onSubmit={this.handleSubmit}>
<input placeholder = "Enter Name" type="text" ref = "name"/>
<button type ="Submit"> Search</button>
</form>
</div>
);
}
}
The component that displays the
class History extends Component {
render() {
return (
<div >
<h2> History</h2>
<ul>
<li>
//display data here
</li>
</ul>
</div>
);
}
}
My container component
class Container extends Component {
constructor(props){
super(props);
this.state ={
history : []
}
this.handleResultChange = this.handleResultChange.bind(this)
}
handleResultChange(history){
this.setState({
history,
})
}
render() {
return (
<div >
<SearchBar onDataFetched={this.handleResultChange}/>
<History data={this.state.history}/>
</div>
);
}
}
You can handle the form submit in the search component in the itself. Pass the search value(input value) to the container component.
class SearchBar extends React.Component {
constructor(props){
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e){
e.preventDefault();
let reqBody = {
name: this.refs.name.value
};
console.log(reqBody);
this.props.onDataFetched(reqBody);
}
render() {
return (
<div >
<form onSubmit={this.handleSubmit}>
<input placeholder = "Enter Name" type="text" ref = "name"/>
<button type ="Submit"> Search</button>
</form>
</div>
);
}
}
class Container extends React.Component {
constructor(props){
super(props);
this.state ={
history : []
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(searchValue){
console.log(searchValue);
}
render() {
return (
<div >
<SearchBar onDataFetched={this.handleChange} />
</div>
);
}
}
ReactDOM.render(<Container />, mountNode )
Now saying that the ideal way to do this would be as mentioned above by using redux and redux forms.
https://redux.js.org/
https://redux-form.com/
This will help you save a lot of time in the future as you will have a correct structure to do it.
I would really suggest you to take a look to redux, it will make your development easier and it's almost a standard implementation for this cases.