React: how to notify parent for changes - reactjs

I'm trying to wrap bootstrap into components with integrated form validation.
short:
Let's say I have
<Form>
<FieldGroup>
<Field rules={'required'}/>
</FieldGroup>
</Form>
Once Field pases validation, how can I notify FieldGroup (parent node) to add a class?
I created a simplified codepen version here
I would like depending on validation status, then change the state of FieldGroup So I can properly change the class name. (add has-warning has-danger etc) and ultimately add class to the Form component.

You need to pass a callback to the child component. I just forked your codepen and added some snippet as below.
http://codepen.io/andretw/pen/xRENee
Here is the main concept,
Make a callback function in "parent" component and pass it to the "child" component
i.e. The child component needs an extra prop to get the callback:
<Form>
<FieldGroup>
<Field rules={'required'} cb={yourCallbackFunc}/>
</FieldGroup>
</Form>
In <FieldGroup /> (parent):
class FieldGroup extends React.Component{
constructor(props){
super(props);
this.state = {
color: 'blue'
}
}
cb (msg) {
console.log('doing things here', msg)
}
render() {
const childrenWithProps = React.Children.map(this.props.children,
child => React.cloneElement(child, {
cb: this.cb
})
)
return (
<div class='fields-group'>
<label> field </label>
{ childrenWithProps }
</div>
);
}
};
In <Field /> (child):
class Field extends React.Component{
constructor(props){
super(props);
this.state = {
empty: true
}
this.validate = this.validate.bind(this);
}
validate(e){
let val = e.target.value;
console.log(!val);
this.setState({empty: !val});
//here to notify parent to add a color style!
// do call back here or you may no need to return.
this.props.cb(val)
return !val;
}
render() {
return (
<div>
<input type='text' onBlur ={(event) => this.validate(event)}/>
{this.state.empty && 'empty'}
</div>
);
}
};
And you can do the things you want in the callback function. (You can also pass a callback from <Form /> to the grandson and get it work, but you need to rethink the design of it is good or not.)

Related

React Send child input data to update parent state

Setup: I have set up a two react components in a parent child relationship. The parent has a state that can be changed by press of a button on parent itself.
Expected behaviour: In the child, I have an input field and I want the state to change to the value I send in the input field on the press of the submit button. I have set up the parent and the child as follows:
What I have tried: I going through this answer and this youtube video but I guess I am not smart enough to make sense of it.
This is what my code looks like
Parent:
class App extends Component {
state = {
value:"someValue"
};
changeValue = (value) => {
this.setState({
value
})
}
render() {
return (
<div>
<p>this is the value from state: {this.state.value}</p>
<button onClick={()=>this.changeValue("valueFromParentComponent")}>Press to change value from parent component</button>
<br/><br/>
<Child getChildInputOnSubmit={()=>this.changeValue()} />
</div>
);
}
}
And this is what the child looks like
Child:
class Child extends Component {
state = {
}
sendChildData = (childInputValue) => {
console.group("This is the data from the input of the child component")
console.log("==============")
console.log(childInputValue)
console.log("==============")
}
render() {
return (
<div>
This is the child component
<br /><br />
<form>
<input type="text" placeholder="Some placeholder"></input>
<button onSubmit={this.sendChildData()} type="submit">Send child's input to parent</button>
</form>
</div>);
}
}
The React behaviour encourages to implement an inverse data flow inside a component hierarchy. Meaning that the child components can receive parent methods through props, this methods will work as callbacks, allowing to receive data, trigger behaviours, update his state and more.
I attach a StackBlitz example, showing how this concept would work in your setup https://stackblitz.com/edit/react-jsv5jo
Edit: Here a few extra tips applied on the example:
To work with inputs on React, a common setup consists on listen the onChange event to receive new data and update the component state. Then, this state is used in the value attribute to update the input content on DOM.
Listen the onSubmit event on the form tag instead on submit button, and remember to add some logic to avoid reloading.
Another good practice on React components is initialize your state object inside the Constructor (In case to be working with a Class Component) and write methods to avoid bloat the render one (Be sure to bind the extra methods on your constructor to avoid invocation problems)
Callbacks are used to pass data from Child component to Parent component in React.
We wright function in Parent component that will receive value and pass this function to child component through Props.
class Parent extends Component {
state = {
value: 'someValue'
};
changeValue = value => {
this.setState({
value
});
};
render() {
return (
<div>
<p>this is the value from state: {this.state.value}</p>
<button onClick={() => this.changeValue('valueFromParentComponent')}>
Press to change value from parent component
</button>
<br></br>
<Child getChildInputOnSubmit={this.changeValue} />
</div>
);
}
}
Now in Child component we call Parents function that we passed in props and send value.
class Child extends Component {
constructor(props) {
super(props);
this.state = {
Childvalue: ''
};
}
handleChange = event => {
event.preventDefault();
this.setState({ Childvalue: event.target.value });
};
sendToParent = () => {
//here calling Parents changeValue
this.props.getChildInputOnSubmit(this.state.Childvalue);
};
render() {
return (
<div>
This is the child Component
<br></br>
<form action='#' onSubmit={this.sendToParent}>
<input
type='text'
placeholder='Some placeholder'
value={this.state.Childvalue}
onChange={this.handleChange}
></input>
<button type='submit'>Send child's input to parent</button>
</form>
</div>
);
}
}

Value not updating inside a ReactJS function

I have a ReactJs function that displays a simple dialogue box, and is intended to update a value for the parent component. The function looks like this:
export function MyDialogue(props: IMyProps) {
var myValue = 0;
return (
<div style={style}>
<label>Enter a number here: </label>
<input type="text" value={myValue} />
<button onClick={() => props.updateFunc(myValue)}>Update</button>
</div>
);
}
I've tried several variations of this; for example, passing in props.myValue, and even changing the function to a class and passing this.state.myValue in. In all but this example, myValue remains as it was in the parent class, and in this version, always 0.
updateFunc simply calls setState on the parent class, and having traced through it, it never gets called with the changed value.
I've seen some documentation that says to essentially handle the onChange event - is this the only way to make this work, or is there a way to implement data binding in React?
Just bind back your input to the parent's state via props.value;
MyDialogue.js
export function MyDialogue(props: IMyProps) {
return (
<div style={style}>
...
<input type="text" value={props.value} />
...
</div>
);
}
Parent.js
....
render(){
const { dialogueValue } = this.state;
return <MyDialuge value={dialogueValue} updateFunc={this.handleUpdate} />
}
You are using uncontrolled input because You are not keeping value of input inside state.
Solution is
With uncontrolled:
export class MyDialogue extends React.Component<IMyProps, {}>{
constructor() {
super();
this.input = React.createRef();
}
return (
<div style={style}>
<label>Enter a number here: </label>
<input type="text" ref={this.input} />
<button onClick={() => props.updateFunc(this.input.current.value)}>Update</button>
</div>
);
}
With controlled:
Maintain myValue state in parent and pass it to child.
and on change on input event call a function of parent which change myValue using setState,
Maintain myValye state inside MyDialogue and onClick pass it to parent.
You need to change this component to a stateful component
then do a two-way binding for your textbox and have it talk to local state and then use that state value to update parent component.
export class MyDialogue extends React.Component<IMyProps, {}>{
constructor() {
super();
this.state = {
myValue: 0
}
}
onChangeHandler = (event:any) =>{
this.setState({myValue:event.target.value});
}
return (
<div style={style}>
<label>Enter a number here: </label>
<input type="text" value={this.state.myValue} onChange={this.onChangeHandler}/>
<button onClick={() => props.updateFunc(this.state.myValue)}>Update</button>
</div>
);
}

How can I get the values of all sibling input checkboxes in a child component?

I have a parent and a child component (Child1) with another child component (Child2). Child2 has checkboxes:
<input name='val1' id='val1' value='val1' type='checkbox' />
<input name='val2' id='val2' value='val2' type='checkbox' />
I want the parent component to be able to get the values of all the checkboxes (probably comma separated). I either want to pass down a handler or have my parent component be able to read all the values
There are a few ways to achieve this, but the simplest would be something like this:
class Child2 extends React.Component {
onChangeCheckbox(event) {
// Update state of child2 to capture current check state of checkbox
// inputs in Child2
this.setState({
[ event.target.name ] : event.target.checked
}, () => {
// Upload the combined/current state of checkbox name/checked pairs
// to Parent via onInputClicked callback prop
this.props.onInputClicked(this.state)
})
}
render() {
return (<React.Fragment>
<input name='val1' id='val1' value='val1' type='checkbox'
onClick={ event => this.onChangeCheckbox(event) } />
<input name='val2' id='val2' value='val2' type='checkbox'
onClick={ event => this.onChangeCheckbox(event) } />
</React.Fragment>)
}
}
class Parent extends React.Component {
render() {
// Render Child2 with checkbox inputs from Parent and pass onInputClicked
// prop as a callback
return (<Child2 onInputClicked={ values => {
console.log('checkbox values in parent', values)
} } />)
}
}
Hope that helps!

office-ui-fabric-react / TextField input properties alternative to onChanged

I'm currently using the TextField from office UI fabric and using the onChanged property to assign my prop in react the value being entered similar to their GitHub example.
However, the event handler is called for each element being entered. How can I make a call to the event handler(this._onChange) only when the user finishes entering the entire text (eg on focus dismiss, etc)?
I'm guessing that would be more efficient than logging an event with each letter being entered.
New to react. Appreciate your help!
This is more of an in-general way React uses the input onChange event. With React, you want to keep the value of your input in state somewhere, whether that is component state or a global store like Redux. Your state and UI should always be in sync. Because of this, the onChange event fires for every character that is entered/removed so that you can update that state to reflect the new value. Inputs written this way are called Controlled Components and you can read more about them and see some examples at https://reactjs.org/docs/forms.html.
That being said, you can detect when the user leaves the input with the onBlur event, though I would not recommend using that to update the state with the value as you'll see that a Controlled Component will act read-only when you don't update the state in the onChange event. You will have to use an Uncontrolled Component, typically setting the initial value with defaultValue instead of value and making things more difficult for yourself.
// CORRECT IMPLEMENTATION
class ControlledForm extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: 'example'
};
this.handleNameChange = this.handleNameChange.bind(this);
}
handleNameChange(e) {
this.setState({
name: e.target.value
});
}
render() {
return (
<div>
<h1>Controlled Form</h1>
<input type="text" value={this.state.name} onChange={this.handleNameChange} />
<p>{this.state.name}</p>
</div>
);
}
}
// INPUT DOES NOT WORK
class BuggyUncontrolledForm extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: 'example'
};
}
render() {
return (
<div>
<h1>Buggy Uncontrolled Form</h1>
<input type="text" value={this.state.name} />
<p>{this.state.name}</p>
</div>
);
}
}
// NOT RECOMMENDED
class UncontrolledForm extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: 'example'
};
this.handleNameChange = this.handleNameChange.bind(this);
}
handleNameChange(e) {
this.setState({
name: e.target.value
});
}
render() {
return (
<div>
<h1>Uncontrolled Form</h1>
<input type="text" defaultValue={this.state.name} onBlur={this.handleNameChange} />
<p>{this.state.name}</p>
</div>
);
}
}
ReactDOM.render(
<div>
<ControlledForm />
<BuggyUncontrolledForm />
<UncontrolledForm />
</div>
, document.getElementById('root'));
<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="root"></div>
You may consider using React's onBlur prop which will be invoked when the input loses focus. Here is an example Codepen which window.alert's the <TextField> component's current value when it loses focus: https://codepen.io/kevintcoughlin/pen/zmdaJa?editors=1010.
Here is the code:
const {
Fabric,
TextField
} = window.Fabric;
class Content extends React.Component {
public render() {
return (
<Fabric>
<TextField onBlur={this.onBlur} />
</Fabric>
);
}
private onBlur(ev: React.FocusEvent<HTMLInputElement>) {
window.alert(ev.target.value);
}
}
ReactDOM.render(
<Content />,
document.getElementById('content')
);
I hope you find that helpful.
References
https://reactjs.org/docs/events.html#focus-events
You can keep your state and UI in sync but use things like your own deferred validation error-check functions to check if the value is good/bad AND/or if you want to do something like logging based on the value only after a certain amount of time passes. Some examples from this page copied below for quick reference - you can do whatever you want in your "_getErrorMessage" function (https://github.com/OfficeDev/office-ui-fabric-react/blob/master/packages/office-ui-fabric-react/src/components/TextField/examples/TextField.ErrorMessage.Example.tsx):
<TextField
label="Deferred string-based validation"
placeholder="Validates after user stops typing for 2 seconds"
onGetErrorMessage={this._getErrorMessage}
deferredValidationTime={2000}
/>
<TextField
label="Validates only on focus and blur"
placeholder="Validates only on input focus and blur"
onGetErrorMessage={this._getErrorMessage}
validateOnFocusIn
validateOnFocusOut
/>

Get current state of child component through prop passed by Parent - Reactjs

Im learning react and very new to this, I am tinkering with something to understand this more.
I would like to know if it is possible to console.log the state of the CHILD using a prop passed down by the parent.
Example :
Child component ( has its own state)
Parentcomponent ( has its own state)
Child Component
this.state={
animal:'Lion'
}
<button onClick{this.props.giveMeState}>
And that, I would want to console the state ( animal:Lion)
Parent Component
this.state={
name: 'John'
}
giveMeState(){ ? what can go here, or is it not that simple ?
)
}
Codepen of example
Parent component cannot query the state of the child component. At least, that's not the intended design of React.
What I think you're asking is how to coordinate the state of child with parent, and you're on the right track to use a prop to pass the state from child to parent.
Perhaps a complete example that does what you want would look like this:
class Parent extends React.Component {
state = { name: "John" }
handleChildAnimal = animal =>
this.setState({ animal });
handleClick = e =>
console.log(`Child animal: ${this.state.animal}`);
render() {
return (
<div>
<Child onAnimal={this.handleChildAnimal} />
<button onClick={this.handleClick}>Tell me Animal state</button>
</div>
);
}
}
class Child extends React.Component {
state = { animal: "Lion" }
handleClick = e => {
console.log(`Animal: ${this.state.animal}`);
this.props.onAnimal(this.state.animal);
}
render() {
return (
<button onClick={this.handleClick}>{this.state.animal}</button>
);
}
}
Demo on CodePen.io
if you want to pass the state value of the child to the parent,
you can do it like this,
In the child component add another function getState and call the reference function giveMeState through this function
...
constructor(props) {
super(props)
this.state={ animal:'Lion' }
this.getState = this.getState.bind(this)
}
getState(){
this.props.giveMeState(this.state)
}
....
<button onClick={this.getState}>
....
and also redefine the parent function so that it takes a parameter
and the console.log that parameter
Not sure if this is a good pattern though
Here is another answer just for giving another example.
It does not fulfill your question and as told fulfilling your question would not be the best approach. Maybe you should try to think differently while working React and states.
App
class App extends React.Component {
state = {
input: "initial input state",
childState: "right now I don't know child state",
};
handleInputChange = e => this.setState({ input: e.target.value });
handleChildState = ( childState ) => this.setState( { childState } )
render() {
return (
<div style={styles}>
<h4>This is parent component.</h4>
<p>Input state is: {this.state.input} </p>
<p>Child state is: {this.state.childState}</p>
<hr />
<Input
onInputChange={this.handleInputChange}
getState={this.handleChildState}
/>
</div>
);
}
}
Child component as Input
class Input extends React.Component {
state = {
myState: "some state"
};
handleSendState = () => this.props.getState(this.state.myState);
handleState = e => this.setState({ myState: e.target.value });
render() {
return (
<div>
<h4>This is Child coponent</h4>
<button onClick={this.handleSendState}>
Click me to get child state
</button>
<p>This is my state: {this.state.myState}</p>
<p>Write something to change child's state.</p>
<input type="text" onChange={this.handleState} />
<p>
Write something to change parent's input state
</p>
<input type="text" onChange={this.props.onInputChange} />
</div>
);
}
}

Resources