Child React Component Will Not show on state change - javascript

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/

Related

ReactJs - unable to update state with setState

I'm having trouble updating my state using setState.
I have a parent component where I define my state, as well as a handleClick function which should update the state.
export default class Main extends PureComponent {
state = {
selectedName: '',
selectedTasks: [],
data: [
{
name: 'John',
tasks: ['vacuum', 'cut grass']
},
{
name: 'Jack',
tasks: ['cook breakfast', 'clean patio']
}
]
handleClick = e => {
console.log('Name: ', e.name);
console.log('Tasks', this.state.data.find(el => el.name === e.name).tasks)
const { selectedName, selectedTasks } = this.state;
this.setState({
selectedName: e.name
selectedTasks: this.state.data.find(el => el.name === e.name).tasks
});
console.log('stateAfter', this.state)
};
render() {
const { data } = this.state;
return (
<div>
<Child
data={data}
handleClick={this.handleClick.bind(this)}
/>
</div>
)
}
}
I have a child component that takes the handleClick as props.
export default class Child extends PureComponent {
constructor(props) {
super(props);
}
render() {
console.log('data', this.props.data);
return (
<ResponsiveContainer width="100%" height={500}>
<PieChart height={250}>
<Pie
data={this.props.data}
onClick={this.props.handleClick}
/>
</PieChart>
</ResponsiveContainer>
);
}
}
When I execute the handleClick function the first time, nothing gets updated. But when I execute it the second time, the state does get updated. What am I missing to make state get updated the first time?
this.setState is asynchronous. If you wish to console.log your state after setState then you'll have to do this via the callback.
this.setState({
selectedName: e.name
selectedTasks: this.state.data.find(el => el.name === e.name).tasks
},
() => {
console.log('stateAfter', this.state)
});

Cannot send event handler to child element through props

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;

Pass input data from child to parent React.js

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>
);
}
}

React form sending empty object

I am trying to send user input in a form component to the component managing the state. I tried to use a callback but my input is not being sent.
Tried to use the form object in a callback
//here is the form component
class ListForm extends React.Component {
constructor(props) {
super(props);
this.state = {
NewItem: {
itemText: "",
id: Date.now(),
completed: false
}
};
}
handleChanges = e => {
this.setState({...this.state,
NewItem: { ...this.state.NewItem, [e.target.name]: e.target.value }
});
};
submitItem = e => {
this.setState({ itemText: "" });
this.props.addItem(e, this.state.NewItem);
};
render() {
return (
<form onSubmit={this.submitItem}>
<input
type="text"
value={this.state.NewItem.itemText}
name="itemText"
onChange={this.handleChanges}
/>
<button>Add</button>
</form>
);
}
}
export default ListForm;
addItem is in the parent component and looks like this. Groceries is just an array of objects with itemText, id and completed
addItem = (e, item) => {
e.preventDefault();
this.setState({
...this.state,
groceries: [...this.state.groceries, item]
});
};
I can enter in the form, but when I hit enter, nothing is added to the list
Very first thing to try is to add e.persist to your addItem() function
addItem = (e, item) => {
e.preventDefault();
e.persist()
this.setState({
...this.state,
groceries: [...this.state.groceries, item]
});
};
Another option is using redux to save the form data in the Global state and then accessing it in the parent component through redux. Here is an example of what that might look like:
import React, { Component } from 'react';
import * as ACTIONS from '../store/actions/actions';
import { connect } from 'react-redux';
class Form1 extends Component {
state ={
value: ''
}
handleChange = (event) => (
this.setState({value: event.target.value})
)
handleSubmit = (event) => {
event.preventDefault()
this.props.input_action_creator(event.target.name.value)
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input id="name" onChange={this.handleChange} type="text" />
<button type="submit"> Submit </button>
</form>
<br />
</div>
)}
}
function mapDispatchToProps(dispatch) {
return {
input_action_creator: (text) => dispatch(ACTIONS.user_input(text))
}
}
export default connect(mapDispatchToProps)(Form1);
class Parent extends Component {
access form data with
this.props.user_Text
}
function mapStateToProps(state) {
return {
user_text: state.user_reducer.user_text
}
}
here is a fully funcitoning react-redux project if you are interested
https://github.com/iqbal125/modern-react-app-sample

How to get data from props.children to its parent in ReactJS?

I'm trying to get data from a props.children[Input] to it's parent[Form], to build an all in one form component. I am currently stuck at getting data to Input component to Form component.
This is what i have done so far =>
edit.js =>
export default class Edit extends Component {
constructor(props) {
super(props)
this.state = {
formFields: {
name: '',
email: ''
}
}
}
render() {
return (
<Form
id={ this.props.match.params.id }
onReceiveFormData={ this.onFiledDataChange.bind(this) }
>
<Input
label='name'
name='name'
value='some'
/>
<Input
label='email'
name='email'
value='email#gmail'
/>
</Form>
);
}
}
Input.js =>
export default class Input extends Component {
constructor(props) {
super(props)
this.state = {
value: this.props.value
}
}
static setValue(val) {
return val ? val : ''
}
handleInputChange = (event) => {
this.setState({
value : event.target.value
})
this.props.onInputChange({
[event.target.name] : event.target.value
})
}
render() {
return (
<input
name={ this.props.name }
value={ Input.setValue(this.state.value) }
/>
);
}
}
Form.js =>
export default class Form extends Component {
constructor(props) {
super(props)
}
saveClick() {
/**
Save logic => I want to get the field data here
**/
}
render() {
return (<div>
{
this.props.children
}
<Button
onClick={ () => this.saveClick() }
>Save</Button>
</div>
);
}
}
For example, If post -> edit page and user -> edit page =>
I would like to do setting the input value inside the Form.js, Form.js will be responsible for setting the values inside those Inputs when it is mounted and it will be responsible for sending the data to the server.
I would like to the know, the scenario I have suggest is possible or is there a better way to do it?
Invert control (make Input a controlled component). Keeping state inside components unnecessarily is generally a bad design practice. Look at the existing <input> it takes a value and an onChange. Your component should do too.
const Input = ({
className,
...rest
}) => (
<input
className={classNames('Input', className)}
type="text"
{...rest}
/>
);
Then the parent has the state for when the form is submitted in Edit.
onFormSubmit = e => {
e.preventDefault();
console.log(this.state.name, this.state.email);
}
onChange = name => e => this.setState({ [name]: e.target.value })
// ...
<Input onChange={this.onChange('name')} value={this.state.name} />
<Input onChange={this.onChange('email')} value={this.state.email} />
If you want Form to listen in on state changes you could do this:
onChange = (e, originalOnChange) => {
console.log('onChange called:', e.target.value);
originalOnChange && originalOnChange(e);
}
// ...
const children = React.Children.map(this.props.children, child =>
React.cloneElement(child, { onChange: e => this.onChange(e, child.props.onChange) })
);
It's a bit unorthodox but technically achievable.

Categories

Resources