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;
Related
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 am having an issue getting a third component to show up using REACT.
I am trying to change the state of workflow, and show Component Three, but I am not sure what I am doing wrong.
After clicking on the continue button, the state changes.
Workflow is changed to WELCOME_MSG, the switch below works.
But I can't seem to return this Component "ComponentThree"
case 'WELCOME_MSG':
return ();
class ComponentOne extends React.Component {
constructor(props) {
super(props)
this.state = {
workflow: 'GET_NAME'
}
this.setWorkflow = this.setWorkflow.bind(this);
}
setWorkflow() {
switch(this.state.workflow){
case 'GET_NAME':
return (<ComponentTwo/>);
case 'WELCOME_MSG':
return (<ComponentThree name={this.state.name} />);
}
}
render() {
console.log('ComponentOne: ',this.state.workflow );
return this.setWorkflow();
/*
switch(this.state.workflow){
case 'GET_NAME':
return (<ComponentTwo/>);
case 'WELCOME_MSG':
return (<ComponentThree name={this.state.name} />);
} */
}
}
// showThree()
class ComponentTwo extends React.Component {
constructor(props){
super(props)
this.state = {
workflow: 'GET_NAME',
name: 'Chris'
}
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
this.setWorkflow = this.setWorkflow.bind(this);
}
handleChange(e) {
//this.setState({name: event.target.name});
e.persist();
console.log('handleChange event.target.name:', e.target);
this.setState((prevState, props) => {
return {
name: e.target.value,
workflow: 'WELCOME_MSG',
}
})
/*
this.setState(state => ({
//name: this.state.name,
name: e.target.value,
//name: "sadfasdf",
})); */
}
setWorkflow() {
console.log("setWorkflow", this.state.workflow);
switch(this.state.workflow){
case 'GET_NAME':
return (<ComponentTwo/>);
case 'WELCOME_MSG':
return (<ComponentThree />);
}
// name={this.state.name}
}
handleClick(e) {
//console.log(this.state);
//e.preventDefault();
console.log('BEFORE handleClick this STATE ON CLICK :', this.state.workflow);
console.log('this.state.name:', this.state.name);
this.setState((prevState, props) => {
return {
workflow: 'WELCOME_MSG',
name: this.state.name,
}
})
return this.setWorkflow();
//this.setWorkflow = this.setWorkflow.bind(this);
/* this.setState(state => ({
//name: this.state.name,
name: this.state.name,
workflow: 'WELCOME_MSG'
})); */
console.log('ON CLICK AFTER SET STATE:', this.state);
//return (<ComponentThree name={this.state.name} />);
// e.preventDefault();
}
render() {
//console.log('this is:', this);
// onChange={this.handleChange}
return (
<div>
<h1>Enter your name</h1>
<div className="grid20 md-grid100">
<input type="text" name="fname" value={this.state.name} onChange={this.handleChange} />
</div>
<div className="grid80 md-grid100">
<button onClick={this.handleClick} >Continue</button>
</div>
</div>
)
}
}
class ComponentThree extends React.Component {
render() {
console.log('ComponentThree this is:', this);
return (
<div className="test">
<h1>Hello {this.state.name}</h1>
<h2>sfasfdadf</h2>
</div>
)
}
}
ReactDOM.render(<ComponentOne />, document.querySelector("#app"))
JS Fiddle Below
https://jsfiddle.net/ameshkin/cvg2rjzo/32/
The state variable to show ComponentTwo or Three is in ComponentOne, but you are updating the state in ComponentTwo, and not updating the state in ComponentOne. To update the state in ComponentOne, you need to pass a function from One to Two in the props.
Edit:
Here's a working fiddle https://jsfiddle.net/j5gxovdm/
Basically having the state in one place, instead of spread across all components (keeping a single source of truth)
return (
this.state.workflow == 'GET_NAME'
? <ComponentTwo
setWorkflow={this.setWorkflow.bind(this)}
onChange={(e) => this.setState({name: e.target.value})}
name={this.state.name}
/>
: <ComponentThree name={this.state.name} />
)
ComponentTwo has its own state and that state is updated upon clicking the button, not the ComponentOne state. As ComponentOne is a container component to switch ComponentTwo and ComponentThree, you have to pass the ComponentOne state as props for ComponentTwo and ComponentThree.
JS Fiddle Below
https://jsfiddle.net/richard929/g0b4d3ur/1/
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> )
}
I am learning the concept of States in React. I am trying to understand the difference between using this.handleChange, and this.state.handleChange.
I would be grateful if someone could explain to me, the exact difference between the two, and why would this.state.handleChange not work?
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
inputValue: event.target.value
});
}
render() {
return (
<div>
< GetInput input={this.state.inputValue} handleChange={this.handleChange} />
{ /* this.handleChanges, and this.state.handleChanges */ }
< RenderInput input={this.state.inputValue} />
</div>
);
}
};
class GetInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Get Input:</h3>
<input
value={this.props.input}
onChange={this.props.handleChange}/>
</div>
);
}
};
class RenderInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Input Render:</h3>
<p>{this.props.input}</p>
</div>
);
}
};
You can technically call this.state.handleChange so long as you add handleChange in your state.
But it doesn't really make sense since you don't want React to keep a track of it, and it will probably not change (unless you are doing some clever tricks).
constructor(props) {
super(props);
this.state = {
handleChange: e => {
e.preventDefault();
console.log("this.state.handleChange");
}
};
}
One would normally declare a member function in a class.
handleChange = e => {
e.preventDefault();
console.log("this.handleChange");
};
Here is the full working code
(working demo available on CodeSandBox).
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
handleChange: e => {
e.preventDefault();
console.log("this.state.handleChange");
}
};
}
handleChange = e => {
e.preventDefault();
console.log("this.handleChange");
};
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button onClick={this.handleChange}>this.handleChange</button>
<button onClick={this.state.handleChange}>
this.state.handleChange
</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
When you say this.state.something this means something is in the state field of the class. When you say this.someFunction this means something is in the class itself. this here is pointing out our class.
class App extends React.Component {
state = {
something: "Something",
}
someFunction = () => console.log(this.state.something);
render() {
return (
<div>
<button onClick={this.someFunction}>Click</button>
</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"></div>
So, you can't use this.state.handleChange since there is no handleChange in the state. It is a function belongs to the class. This is why we use this.handleChange.
you can store a function in state
constructor(super){
super(props)
this.state = {
generateANumber: () => this.setState({ number: Math.floor(Math.random() * 100) }),
number: 0
}
}
then if you want to call it in your render method
render() {
return <p> {this.state.number} <button onClick={() => this.state.generateANumber()} Press Me To Generate A New Number </button> </p>
}
This is the concept of storing a function in state. This.function just means the function belongs to that class so you can use it using the this keyword.
I'm using redux-form and formValueSelector to get input values from fields. I want to get value on change, but ##redux-form/CHANGE action is called after my function called on onChange. So I get not updated value. My code:
export class PersonFilter extends React.Component {
constructor(props) {
super(props);
}
filterByName = (event, searchName) => {
//here searchName is getting old value
store.dispatch({type: 'PERSON_FILTER_BY_NAME', payload: {name: searchName}});
};
render() {
const {
searchName
} = this.props;
return (
<form className="person person--filter" onSubmit={e => {
e.preventDefault();
this.filterByName(e, searchName)
}}>
<Field
onChange={e => {
this.filterByName(e, searchName)
}}
className="person__input" icon="search"
name="searchName" component={renderField} type="text"
placeholder="Name"/>
</form>
);
}
}
PersonFilter = reduxForm({
form: 'filter',
initialValues: {
searchName: store.getState().personsFilterReducer.filterByName
}
})(PersonFilter);
const selector = formValueSelector('filter');
PersonFilter = connect(state => {
const searchName = selector(state, 'searchName');
return {
searchName
}
})(PersonFilter);
and wrapper component:
class Persons extends React.Component {
constructor() {
super();
}
render() {
return (
<div>
<PersonFilter/>
</div>
};
}
}
Instead of just passing onChange to the action you should setState, firstly initialise the state this.state inside the wrapper component constructor and make sure you pass props to the constructor as well , and then setting a state inside onChange function this.setState and binding it.
Something like this:
Wrapper Component
class Persons extends React.Component {
constructor (props) {
super(props);
this.state = {}
}
onChange(field, value) {
this.setState({[field]: value});
}
render () {
return <PersonFilter onChange={this.onChange.bind(this)} />
}
}
And inside your PersonFilter component make a function onFieldChange which will pass the reference to the function onChange inside wrapper component to set the state this.setState
export class PersonFilter extends React.Component {
constructor(props) {
super(props);
}
onFieldChange(event) {
const changeName = event.target.name;
const changeValue = event.target.value;
this.props.onChange(fieldName, fieldValue);
}
and then finally
<Field
onChange={this.onFieldChange.bind(this)}
className="person__input" icon="search"
name="searchName" component={renderField} type="text"
placeholder="Name"/>