essentially I have a form component for users to fill out. When they complete an action: onChange={onChange} it returns a value (child component);
function onChange(value) {
console.log("This is: ", value);
}
I want to pass the value to a state in the parent component (so that the from can be processed and form data sent). How can I do this? My constructor looks something like this (parent component);
constructor(props) {
super(props);
this.state = {
form: {
name: '',
age: '',
value: ''
}
};
}
Trying to learn react, please go easy as I'm unsure of how to do this. Would love any feedback! Thanks!
Please read the official documentation start guide carefully and patiently when you are a starter.
In react component, state is the internal state and props is the state passed from outside.
You could only modify the state by setState method.
As for the question you ask, you could define a callback function which call setState in parent component, then pass the callback function as props into the child component.
As Zhili said, you should define a function in your parent component that is passed as prop to the child component.
Here's a brief example:
const Child = ({ onSubmit }) => (
<form onSubmit={onSubmit}>
{ /* your <input/>'s here ... */}
</form>
);
class Parent extends React.Component {
state = {
value: null,
};
onChildSubmit = (value) => {
this.setState({
value,
});
}
render() {
return (
<div class="Something">
<Child onSubmit={} />
</div>
)
}
}
Related
I'm trying to learn react and ran into a snag. I'm struggling to update the parent based on the child state. I've managed to pass the child state to the parent by binding the child's state to the same child's prop when invoked by the parent.
Parent.js
import React, { Component, setState } from 'react'
import './Parent.css'
import Child from './Child'
export class Parent extends Component {
constructor(props) {
super(props)
this.state = {
childState: false
}
}
checkState(newState){
console.log(`new state is ${newState}`)
}
render() {
return (
<div class={`parent ${this.state.childState ? 'parent-child-not-clicked' : 'parent-child-clicked'}`}>
<h1>{this.state.childState === true ? 'true' : 'false'}</h1>
{/* <Child changeState={(newState)=>{newState === true ? this.setState(prevState => ({childState: prevState.childState+1})):this.setState(prevState => ({childState: prevState.childState-1}))}}></Child> */}
<Child changeState={(newState) => {console.log(newState)}}></Child>
</div>
)
}
}
export default Parent
Child.js
import React, { Component } from 'react'
import "./Child.css"
export class Child extends Component {
constructor(props) {
super(props)
this.state = {
childState: false
}
this.updateState = this.updateState.bind(this)
}
updateState(){
this.setState({
childState: !this.state.childState
}, () => {return this.state.childState})
}
render() {
return (
<div className="child">
<h1>{`child state is ${this.state.childState}`}</h1>
<div onClick={() => this.props.changeState(this.updateState())}>Click</div>
</div>
)
}
}
export default Child
The console keeps rendering undefined, meaning newState doesn't contain the boolean value true / false. Would appreciate if anyone can point me in the right direction.
Thanks in adavance
this.updateState() doesn't return anything. So nothing is sent to this.props.changeState.
Probably the simplest approach is to remove this.props.changeState from the JSX markup and move it into updateState. Then within updateState define the new state object, update the component's state with it, and pass it to the prop function. Something like this:
updateState(){
const newState = {
childState: !this.state.childState
};
this.setState(newState);
this.props.changeState(newState);
}
Then in the JSX just call updateState (putting less logic inline in the JSX and more in the functions):
<div onClick={this.updateState}>Click</div>
As an aside, while the example shown is clearly a contrived one, tracking the same state in two different places is probably the wrong design. If the parent just needs updates, pass it just the updates that it needs. But if the parent is tracking the state, the child doesn't need to duplicate that effort. You can remove state from the child entirely and just pass it the values it needs, simplifying the whole thing.
I have an array which I want to save in my database. I have a page (parent component) and a form (child component) where my birthday input is (the one I'm saving in database). The select html elements are in the child component, and I take their values after every change. Now I need to pass recieved values from select elements back to my parent component and update the array with the recieved props. I will try to recreate my code as best as I can:
AuthenticationPage.js (Parent):
import React from 'react';
class AuthenticationPage extends Component {
constructor(props) {
super(props);
this.state({
setMonth:null
setDay:null
setYear:null
})
}
render() {
return(
<div>
<SignupForm // This is where I call my child component
onChange={(monthValue) => this.setState({ setMonth: monthValue })}
initialValues={{
dateofbirth: [
{
month: this.state.setMonth, // This one is okey but I can use onChange just to change one state
day: this.state.setDay,
year: this.state.setYear
}
]
}}
/>
</div>
)
}
}
export default AuthenticationPage;
SignupForm.js (Child):
import React from "react";
import SelectSearch from "react-select-search";
const SignupForm = (props) => (
<FinalForm
{...props}
render={(fieldRenderProps) => {
const {
// Here I render props from parent component
} = fieldRenderProps;
function monthPicker(monthValue) {
props.onChange(monthValue);
// How can I update the state of setDay and setYear states in parent component
}
return (
<Form className={classes} onSubmit={handleSubmit}>
<SelectSearch
options={month}
onChange={(monthValue) => monthPicker(monthValue)} // This is ok, I change setMonth state in parent with this function
/>
<SelectSearch
options={day}
// How to work with this, this is day input
/>
<SelectSearch
options={year}
// How to work with this, this is year input
/>
</Form>
);
}}
/>
);
export default SignupForm;
So basically I want to update states in parent component after onChange happens on select elements in my child component. I'm new to React and I can't figure this out whole day, so any help will mean a lot.
Child should receive a 'onChange' function prop. That will be called inside the child component, every time the values on the form are changed (this.props.onChange(newValue)).
The parent should hold a state of the values that will be updated accordingly (<SignupForm ... onChange={(newValue) => this.setState({ value: newValue })} />)
From parent to child you can pass data through props, but from child to parent best way is by function , i ll try to write an example below, i always code with functional component so my syntax won't be right below, but i hope you ll get the idea ...
Parent Component
class Parent extends React.Component {
constructor(props) {
super(props);
this.state({
monthValue:null
})
}
// this function send as a prop
const updateMonthValue =(value)=>{
this.state.monthValue=value
}
render() {
return <Child updateMonthValue={updateMonthValue} />;
}
}
Child Component
const Child =(props) => {
const submitHandler =(value) =>{
//here you can call the function of parent and the function in the parent will update state of parent
props.updateMonthValue(value)
}
render() {
return <h1><button onClick={()=>submitHandler("june")} /></h1>;
}
}
I am calling a handle method (to change state) in a <grandchild> component but it stop rendering after a couple of callback in the <grandparent> component.
I have tried to:
setting bind correctly with both this.bind in construct and arrow method.
making sure the call back is call everytime the prop.callback is call.
This is an example of what I'm trying to do with graphql server:
Grandparent Component
//Graphql constant for query
const ApolloConstant = gpl`
...etc
class Grandparent extends Component {
constructor(props) {
super(props);
this.state = { vars: 'query_string' }
}
handler = (args) => {
this.setState({vars: args})
}
render() {
return (
// For requerying graphql with search
<input onChange={() => this.setState(vars: e.target.value)} />
<Query variable={this.state.vars}>
...code -> some_data_arr
{<ChildComponent data={some_data_arr} handler={this.handler}/>}
</Query>
);
}
}
Child Component
//This component will take in an arr of obj and display a obj list
// if one of the obj is clicked then render a child component to display that single obj
class Child extends Component {
constructor(props) {
super(props);
this.state = {
singleData: null
}
}
render() {
return (
// Ternary operator here for conditional rendering
{
this.state.singleData
? <Grandchild data={this.state.singleData} handleParentData={this.props.handler} />
: this.display_data(this.props.data)
}
);
}
//Method to call to display objects
display_data = () => {
this.props.map() =>
<div onClick={this.setState({singleData: data})} > ...some code to display data <div/>
}
}
Grandchild Component
class Grandchild extends Component {
render() {
return (
{...do some code with object props here}
<Button onclick={(this.props.handleParentData(vars))} >Btn</Button>
);
}
}
When I test this, everything works for the first 3-4 render then no more re-rendering even though the callback is going through. I check to see if the method in <grandparent> is being call and it does but the state stop changing. Even after going to a new route (react router) and then coming back, I still cant change state with that callback.
<Button onclick={(this.props.handleParentData(vars))} >Btn</Button>
I think the problem is the function being called right into the onclick prop, you should probably have it wrapped in another function so it is only called when you actually trigger the onclick listener:
handleClick = () => {
this.props.handleParentData(vars)
}
render() {
return (
{...do some code with object props here}
<Button onclick={(this.handleClick)} >Btn</Button>
);
}
I have multiple component with similar piece code in lifecycle methods and some similarity in state variables. Is there a way to unify them, by inheriting from one parent or something like that?
constructor(props) {
super(props);
this.state = {
//state properties similar in all components, getting from redux
//state properties specific for this component
}
// same code in many components
}
componentWillMount() {
// same code in many components
// code specific for this component
}
Can I use children methods and props in parent "wrapper" ? Can I change component state from parent ?
You can create Higher Order Component (HOC) for that, basically, you just write component with your same lifecycle method which is repeating, and then in render() function, call this.props.children function with any HOC internal state arguments you want, you can pass the whole state and a setState function as well, so you can change the HOC's state inside the underlying component.
For example:
class HOC extends React.Component {
constructor(props) {
super(props);
state = {
someState: 'foo',
};
}
componentWillMount() {
console.log('i mounted!')
}
render() {
return (
<div>
{this.props.children({ state: this.state, setState: this.setState })}
</div>
)
}
}
const SomeComponent = () =>
<HOC>
{({ state, setState }) => (
<div>
<span>someState value: </span>
<input
value={state.someState}
onChange={e => setState({ someState: e.target.value})}
/>
</div>
)}
</HOC>
You can also do really cool and interesting things with it, like connecting a slice of your redux state whenever you need it:
import { connect } from 'react-redux';
const ProfileState = connect(
state => ({ profile: state.profile }),
null,
)(({
profile,
children
}) => (
<div>
{children({ profile })}
</div>
));
const ProfilePage = () => (
<div>
Your name is:
<ProfileState>
{({ profile }) => (
<span>{profile.name}</span>
)}
</ProfileState>
</div>
);
Here is the full documentation on this technique.
You could create HOCs (Higher Order Components) in that case. It can look like this:
/*
A Higher Order Component is a function,
that takes a Component as Input and returns another Component.
Every Component that gets wrapped by this HOC
will receive `exampleProp`,`handleEvent`,
plus all other props that get passed in.
*/
function WithCommonLogic(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
example: ''
}
}
componentWillMount() {
...
// Same code in many components.
}
callback = () => {
/* Enhanced components can access this callback
via a prop called `handleEvent`
and thereby alter the state of their wrapper. */
this.setState({example: 'some val'})
}
render() {
return <WrappedComponent
exampleProp={this.state.example}
handleEvent={this.callback}
{...this.props}
/>
}
}
// You use it like this:
const EnhancedComponent1 = WithCommonLogic(SomeComponent);
const EnhancedComponent2 = WithCommonLogic(SomeOtherComponent);
Now all the shared logic goes into that HOC, which then wrap all your different components you want to share it with.
See the React Docs for further reading.
Check the code here
jsfiddle
I wish to update the value property of individual item from the Child component. But as props are immutable and don't trigger re-render the code doesn't work. One way I know to make this work is pass a function from GrandParent to Parent and then to Child and use it to update state of GrandpParent. This will trigger re-render in the Child component. But this also causes re-render of GrandParent, Parent and other siblings of Child component.
// comment
Is there a better way to do this, this doesn't seem optimal to me.
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleClick = this.handleClick.bind(this)
}
handleClick(e) {
this.props.handleIncrement(e.currentTarget.dataset.key)
}
render() {
return (
<div>
<span>{this.props.item.value}</span>
<button data-key={this.props.item.key} onClick={this.handleClick}>inc</button>
</div>
);
}
}
class Parent extends React.Component {
render() {
return (
<div>
{
this.props.list.map((item) => <Child item={item} handleIncrement={this.props.handleIncrement} />)
}
</div>
);
}
}
class GrandParent extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [
{
key: 'one',
value: 1
},
{
key: 'two',
value: 2
},
{
key: 'three',
value: 3
}
]
};
this.handleIncrement = this.handleIncrement.bind(this)
}
handleIncrement(key) {
this.setState({
list: this.state.list.map((l) => {
if (l.key === key) {
return {key: l.key, value: l.value + 1}
}
return l
})
})
}
render() {
return (<Parent list={this.state.list} handleIncrement={this.handleIncrement} />);
}
}
React.render(<GrandParent />, document.getElementById('container'));
You have to pass the handler from the Grand parent and call this handler whenever you wanted to increment. Read about coupling and cohesion for theoretical background.
React is based on the concept of unidirectional data flow. This means that your are passing data down to other components who receive it as props and render it, or passing it down to another sub component.
However, sometimes we want a child component to let a parent component that something happened. To solve this, we use callback. Callbacks are functions that we can pass as props to a child component, so he can use them we something happens. A classic example is to pass an onClick handler to a child component that has a button. Then, when the button is pushed the child component calls it like this:
this.props.onClick()
letting the parent know that the button was clicked. This will work for yor example too. Create a function in the GrandParent component that knows how to increment the value.
incrementValue = (idx) => {
// Copy the list to avoid mutating the state itself.
let newList = this.state.list.slice();
newList[idx].value += 1;
this.setState({list: newList});
}
Then pass this function as callback
<Parent onClick={this.incrementValue}/>
Then bind it to the button click like this:
<button onClick={this.props.onClick}>inc</button>
Read this to learn more about state and props in React.