Refreshing children state from parent React - javascript

I have a table with some data and each element in the table is a React class component. It looks like this:
All i want is to have one checkbox for "check all" feature (top left checkbox). The thing is I don't know how to solve that because of props and state.
I have code like that in single element component:
getInitialState: function() {
return { component: this.props.data };
},
render: function() {
var data = this.state.component;
data = data.set('checked', this.props.data.get('checked'));
...
}
And I know I shouldn't get checked param from props but it is just temporary.
What I have problem with is: When I update checked param in parent it doesn't update state, because getInitialState isn't called after refresh (yep, i know it should be like that).
My question is: can I somehow update state of child component? Or it is better way to achieve that.

With functional components:
An easy way to refresh the children internal state when props provided by parent change is through useEffect():
In the children:
const [data, setData] = useState(props.data);
useEffect( () => {
setData(props.data);
}, [props.data]);
In this way everytime the props.data change the useEffect will be triggered and force to set a new status for some data and therefore the component will "refresh".

My approach is that you should have structure something like this in parent's render:
<ParentView>
{ this.props.rows.map(function(row) {
<ChildRow props={row.props} />
}).bind(this)
}
</ParentView>
And then on row.props you have the information whether current row item is checked or not. When parent checkbox is toggled, you populate all the row.props with the status.
On child, you will receive those with componentWillReceiveProps and you do the magic (e.g. set the correct state) when checkbox is toggled:
componentWillReceiveProps: function(props) {
this.setState({isChecked: props.isChecked});
}
(Info from the React's docs: Calling this.setState() within this function will not trigger an additional render.)
Child element's render would be something like:
<div>
<input type='checkbox' checked={this.state.isChecked} />
<label>{this.props.label}</label>
</div>

You can solve this by storing the checked state of all child elements in the parent only. The children set their checked status based on props only (they don't use state for this) and call a callback supplied by the parent to change this.
E.g., in the child:
render: function() {
//... not showing other components...
<input type="checkbox"
value={this.props.value}
checked={this.props.checked}
onClick={this.props.onClick}>
}
The parent supplies the onClick, which changes the checked status of the child in its state and passes this back to the child when it re-renders.
In the parent:
getInitialState: function() {
return {
allChecked: false,
childrenChecked: new Array(NUM_CHILDREN) // initialise this somewhere (from props?)
}
},
render: function() {
return <div>
<input type="checkbox" checked={this.state.allChecked}>
{children.map(function(child, i) {
return <Child checked={this.state.childrenChecked[i]}
onClick={function(index) {
return function() {
// update this.state.allChecked and this.state.childrenChecked[index]
}.bind(this)
}.bind(this)(i)}
/>
}).bind(this)}
</div>;
}
-- not checked for typos etc.

Please see the react documentation on Lifting State Up.
In your child component, you need to use the props. To update the prop, you need to provide an update function from the parent.

Related

Semantic-UI React Checkbox state not working

I've got a parent container that calls a class-based react component child (since the semantic-ui react docs are all written with class-based components). The child component has a Form.Field:
<Form.Field
control={Checkbox}
label={{children: 'cardreader'}}
checked = {this.state.cardReaderChecked}
onChange={this.cardReaderToggleHandler}
/>
I've got a state:
state = {
cardReaderChecked: false,
}
and a cardReaderToggleHandler:
cardReaderToggleHandler = () => {
console.log(this.state.cardReaderChecked);
this.setState((prevState, props) => ({
cardReaderChecked : !prevState.cardReaderChecked
}))
console.log(this.state.cardReaderChecked);
}
I've tried toggling on this.state.cardReaderChecked but I found a lot of references and docs recommending this approach with the prevState to avoid the delay in state updating.
However, there must a logical fault because it doesn't work. If I refresh the page, the checkbox is clear as this.state.cardReaderChecked.
The first time I click the checkbox it renders with the tick, and my this.state.cardReaderChecked updates to true (according to my trusty React tools in Chrome). However, both console.log printouts give me a false and if I pass my state back to the parent form, it also shows that the checkbox is false.
Every subsequent click toggles but a ticked checkbox shows a true state but passes on a false to the parent form (that's where the console.log is currently) and vice versa an unticked checkbox passes back a true.
I'm almost tempted to remove the not from the prev.state in the setState, but I would prefer to understand why this happens.
Bonus Question: How can I query the checkbox state checked in a functional component?
Pass the function to a child as prop
class Parent extends Component {
state = {
cardReaderChecked: false,
}
cardReaderToggleHandler = () => {
this.setState((prevState, props) => ({
cardReaderChecked : !prevState.cardReaderChecked
}), () => console.log(this.state.cardReaderChecked))
}
....
render () {
return (
....
<Form.Field
control={Checkbox}
label={{children: 'cardreader'}}
checked = {this.state.cardReaderChecked}
onChange={this.cardReaderToggleHandler}
/>
)
}

React state lifecycle for a filter component

I'm just looking for advice on how to properly set / read state in a component that is just a filter (i.e. select dates, min max values, etc).
I basically have:
onMinDateChange(minDate) {
this.setState({minDate});
},
onMaxDateChange(maxDate) {
this.setState({maxDate});
},
...
Now I want to call this.props.onChange() on every state change, but I have two issues:
state doesn't immediately update; how do I call this on the "next tick"? componentDidUpdate?
I'm not sure how to observe any state change so that I don't have to write:
onMinDateChange(minDate) {
this.setState({minDate});
this.update();
},
onMaxDateChange(maxDate) {
this.setState({maxDate});
this.update();
},
...
Any help on both of these points?
You can pass a callback to the this.setState(). see below:
_onStateUpdate() {
*Some code here*
}
onMinDateChange(minDate) {
this.setState({minDate}, _onStateUpdate);
},
Regarding the both of your issues, including this one:
I'm not sure how to observe any state change
You can use componentDidUpdate( prevProps, prevState ) callback, and determine inside, whenever the state was changed.
https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate
Here you're trying to synchronize your local state with upper components. That's possible, but it's a hard thing to do it right. Especially when you will occasionally need to set some filter values from the top. Consider moving the state of the filter to the upper component.
To do that you will need to pass your state object and function used to update it instead of setState as component props. State object would be stored as a part of the upper component state then.
You can use value links to make it look elegant. Here's what you code will look like in this case:
import { Link } from 'valuelink'
import { Input } from 'valuelink/tags.jsx'
const UpperComponent = React.createClass({
getInitialState(){
return {
filter : {
text : '',
...
}
}
},
render(){
return (
...
<Filter state={ Link.state( this, 'filter' ) } />
)
}
});
const Filter = ({ state }) => (
<div className="filter">
<Input valueLink={ state.at( 'text' ) } />
...
</div>
)
Here's an explanation of the technique:
https://medium.com/#gaperton/state-and-forms-in-react-part-3-handling-the-complex-state-acf369244d37#.nuloz9adx
And here's the lib:
https://github.com/Volicon/NestedLink

Access child component state in other ways than ref?

I am unable to use something like this.refs.child.state in my application to access state of a child component, hence need an alternative way to do so. Main reason for this is to pass child contents to redux state when a certain button is clicked inside such childs parent component, hence function in parent component needs to pass childs content as one of the parameters.
Depending on the structure of your components (hard to tell when you don't post code), you could fix this just by chaining callbacks via props. I.e.
var Parent = React.createClass({
onChange: function(childValue){
this.setState({childValue: childValue});
},
render: function(){
return <Child onChange={this.onChange} />
}
});
var Child = React.createClass({
handleChange: function(event){
this.props.onChange(event.target.value);
},
render: function(){
return <input onChange={this.handleChange}/>
}
});
Add in as many middle-layers as needed of the form;
var MiddleChildA = React.createClass({
render: function(){
return <MiddleChildB onChange={this.props.onChange} />
}
});

Input type text's value not getting updated while using React JS

I am using React JS to render an input type="text". I know that if you use the value property React renders a readonly textbox. So, I wrote a small component of my own(see below).
React.createClass({
getInitialState: function() {
var self = this;
return {value: self.renderDefault(self.props.value, '')};
},
handleOnChange: function(event) {
this.setState({value: event.target.value});
if (this.props.onChange){
this.props.onChange(event);
}
},
renderDefault : function(value, defaultValue){
return typeof value !== 'undefined' ? value : defaultValue;
},
render: function() {
var value = this.state.value;
return (<input type="text"
size={this.renderDefault(this.props.size, 1)}
value={value}
onChange={this.handleOnChange}
placeholder={this.renderDefault(this.props.placeholder, '')}
/>);
}
});
Every time I try to render this component with a different value I don't see the component getting updated with the updated value.
Everytime I try to render this component with a different value I don't see the component getting updated with the updated value.
You mean you are running
<MyComponent value={someValue} />
with different values?
If that's the case, the component does not use the new value because you are not telling it to.
The component keeps its state between rerenders and the value shown in the text field comes from the state. If you don't update the state based on the new props, nothing will change. You have to implement componentWillReceiveProps:
componentWillReceiveProps: function(nextProps) {
this.setState({value: nextProps.value});
}
From the docs:
Invoked when a component is receiving new props. This method is not called for the initial render.
Use this as an opportunity to react to a prop transition before render() is called by updating the state using this.setState(). The old props can be accessed via this.props. Calling this.setState() within this function will not trigger an additional render.
More about lifecycle methods.

How to pass state with parent to child component

Is there any way passing state from parent component to child component like:
var ParentComponent = React.createClass({
getInitialState: function() {
return {
minPrice: 0
}
},
render: function() {
return (
<div onClick={this.doSomething.bind(this, 5)}></div>
);
}
});
var ChildComponent = React.createClass({
getInitialState: function() {
return {
minPrice: // Get from parent state
}
},
doSomething: function(v) {
this.setState({minPrice: v});
},
render: function() {
return (
<div></div>
);
}
});
I want to change parent state value from child component. In react.js is it possible or not?
There is but it's not intended to work like that in React.
2-way data binding isn't the way to go in React, excerpt from the docs.
In React, data flows one way: from owner to child.
So what you want to do if you want to manipulate parent state in your child component is passing a listener.
//parent component's render function
return (
<Child listenerFromParent={this.doSomething} />
)
//child component's render function
return (
<div onClick={this.props.listenerFromParent}></div>
)
You can use the limelights solution, ie passing a function from the parent to the child.
Or you can also use projects like React-Cursor which permits to easily manipulate state passed from a parent component in a child.
I have made my home made framework (Atom-React, some details here) that also use cursors (inspired by Om), and you can somehow achieve easily 2-way data binding with cursors permitting to manipulate the state managed by a parent component.
Here's an exemple usage:
<input type="text" valueLink={this.linkCursor(this.props.inputTextCursor)}/>
The inputTextCursor is a cursor passed from a parent to a child component, and thus the child can easily change the data of the parent seemlessly.
I don't know if other cursor-based React wrappers use this kind of trick but the linkCursor function is implemented very easily with a simple mixin:
var ReactLink = require("react/lib/ReactLink");
var WithCursorLinkingMixin = {
linkCursor: function(cursor) {
return new ReactLink(
cursor.getOrElse(""),
function setCursorNewValue(value) {
cursor.set(value);
}
);
}
};
exports.WithCursorLinkingMixin = WithCursorLinkingMixin;
So you can easily port this behavior to React-Cursor

Categories

Resources