React.js - Changing state of owner component by an event handler - javascript

Is this the correct way to handle the model change event -
a. The handleModelChange function is being passed as onModelChange prop to SubClass.
b. When the model change event triggers, for the re-render to occur, the handler from the SubComponent changes the state of the MainComponent.
var _SomeMixin={
componentWillMount: function() {
this.props.options.model.on("MODEL_CHANGED", this.props.onModelChange);
},
componentWillUnmount: function() {
this.props.options.model.off("MODEL_CHANGED", this.props.onModelChange);
},
/* more mixin functions */
}
var SubComponent = React.createClass({
mixins: [_SomeMixin],
render: function() {
return (
<div>
<!-- ... more elements .. >
</div>
);
}
});
var MainComponent = React.createClass({
getInitialState: function() {
return {MainComponentState: []};
},
handleModelChange: function() {
if (this.isMounted()) {
this.setState({MainClassState: this.props.options.model.toJSON()});
}
},
render: function() {
return (
<SubClass options={this.props.options} onModelChange={this.handleModelChange} />
);
}
});

This is one of possible ways to inform parent component that inner component has been changed. But this approach will lead you to a callbacks hell, that you will have to pass every time.
Better solution is to use some state management library like Moreartyjs

Related

Reactjs - parent, child action and functions

I am working on a reactjs application - and I am breaking up a big component to have a child component. I've created a callback function in the child that will go back to the parent. When a checkbox is checked -- the child component does the callback and this goes back into the parent shell -- however I want to now jump out of the event callback and push the data to an original parent function.
my application kind of looks like this on a streamlined level.
var Parent = React.createClass({
onSelect: function(value, flag){
this.updateSelected(value, flag);
}
updateSelected: function(value, flag) {
let array = this.state.selectedArray;
array.push({"value": value, "flag": flag});
this.setState({
selectedArray: array
});
},
render: function() {
return (
<div>
<Child onSelect={this.changeHandler} />
<span>{this.state.value}</span>
</div>
);
}
});
var Child = React.createClass({
selectHandler: function(e) {
this.props.onSelect(e.target.value, false);
},
render: function() {
return (
<input type="checkbox" onSelect={this.selectHandler} />
);
}
});
but I can not just invoke the "this.updateSelected(value, flag);" as its inside the this scope.
You should use ES6 syntax and JSX syntax instead of pure React API, that help you a lot to reduce this kind of error about scoping and become your code more readable, but If you even want to use that syntax, so you should bind the function once you pass down to the child component , you can do it like this:
var Parent = React.createClass({
onSelect: function(value, flag){
this.updateSelected(value, flag);
}
updateSelected: function(value, flag) {
let array = this.state.selectedArray;
array.push({"value": value, "flag": flag});
this.setState({
selectedArray: array
});
},
render: function() {
return (
<div>
<Child onSelect={this.changeHandler.bind(this)} />
<span>{this.state.value}</span>
</div>
);
}
});
as you can see adding the .bind method and passing as argument the context that you want your function execute, then once your function be invoked, the scope of that function will be the parent component instead of child component.
Remember that this approach bind method could affect the improve of your component if your Parent component rerenders many times.
If you are using JSX syntax you should do something like
class YourComponent extends React.Component {
changeHandler = (value, flag) => {
this.updateSelected(value, flag);
}
render() {
return (
<div>
...
<Child onSelect={this.changeHandler} />
</div>
);
}
}

In a react class: How do I pass a variable inside same component

I'm learning ReactJS and need to pass a variable inside the same component.
Here's an example
var DataBase = [
{
position: 1
},
{
position: 2
},
{
position: 3
},
{
position: 4
}
];
var Component = React.createClass({
getDefaultProps: function() {
var counter = 0;
},
componentDidMount: function() {
var dbPos = this.props.db[counter+1].position;
return dbPos;
},
render: function () {
return (
<div className="Component">
{this.dbPos}
</div>
);
}
});
ReactDOM.render(
<Component db={DataBase} />,
document.getElementById('main')
);
So, this obviously doesn't work. What I need is to pass var dbPos created in componentDidMount to the render (without any events like onClick). This will be time driven, like 10 seconds in each position with setTimeout().
Is this possible? How? Is there a better solution? I'll appreciate all your help.
That problem may regard state handling. There are multiple ways to handle the application's state in a React application, but I will assume that you are interested in keeping dbPos as part of the component's state (and that you may be mutating it in the future). To achieve this, simply use this.setState and this.state.
Before I show the example, I will state a few other mistakes in your code snippet:
getDefaultProps should return a hash object of the props, not declare them with var (that would make them scoped to the method rather than the component instance)
counter, as a prop, must be referred as this.props.counter. Note that counter is not part of this component's state, and can only change with a respective change of that prop in an upper level of the component tree.
With that in mind:
var Component = React.createClass({
getDefaultProps: function() {
return {counter: 0};
},
componentDidMount: function() {
var dbPos = this.props.db[this.props.counter+1].position;
this.setState({ // change the state of this component
dbPos: dbPos
})
},
render: function () {
return (
<div className="Component">
{this.state.dbPos}
</div>
);
}
});
If you do not want dbPos to mutate as part of the component's state, simply make a new method for retrieving the intended position. No mutable state will be involved here.
var Component = React.createClass({
getDefaultProps() {
return {counter: 0};
},
componentDidMount() {
// no longer needed
},
getPosition() {
return this.props.db[this.props.counter + 1].position;
},
render () {
return (
<div className="Component">
{this.getPosition()}
</div>
);
}
});

React not updating component props?

It seems that my component props are not updated the way I thought they ought to be.
var AdditionalInfoBlock = React.createClass({
getInitialState: function() {
return ({
comment: this.props.comment
});
},
onCommentChange: function(e) {
this.setState({comment: e.target.value});
this.props.onCommentChange(e);
},
render: function() {
return (
<ToolBlock>
<div className="container">
<form>
<textarea value={this.props.comment} onChange={this.onCommentChange} />
</form>
</div>
</ToolBlock>
);
}
};
var MainTool = React.createClass({
getInitialState: function () {
return {
comment: undefined
};
},
restart: function (e) {
e && e.preventDefault && e.preventDefault();
this.setState(this.getInitialState());
},
onCommentChange: function(e) {
this.setState({
comment: e.target.value
});
},
render: function() {
return (
<div>
<AdditionalInfoBlock comment={this.state.comment}
onCommentChange={this.onCommentChange} />
</div>
);
}
};
What I want this code to do is basically hold the comment's value until I post it and call restart - then it should reset the value in both AdditionalInfoBlock and MainTool. At the moment after restart is called when I console.log() the state of MainTool, the comment value is undefined. However, if I log AdditionalInfoBlock state and/or props, then the comment value is not reset in neither of those.
(PS. This is obviously a short version of the code, hopefully only incorporating the relevant bits. Although restart is not called in this excerpt, it doesn't mean that I have forgotten to call it at all :))
Since you're handling the same state on both components MainTool and AdditionalInfoBlock, finding value of the comment can get confusing. While you're listening for the comment change, you're setting the state on both components.
The changed state in your parent component MainTool is passing the props to the child AdditionalInfoBlock. getInitialState is invoked once before mounting (getInitialState documentation). Therefore, the passed on property is not handled by you child component on succeeding updates. By using componentWillReceiveProps, you will be able to handle the props sent by MainTool.
var AdditionalInfoBlock = React.createClass({
...
componentWillReceiveProps(nextProps) {
this.setState({comment: nextProps.comment});
},
Working Code: https://jsfiddle.net/ta8y1w1m/1/

Reactjs how to change the state of a component from a different component

I have a react component, lets call it as component 1
define([..., /path/component_2.jsx], function(..., Component2) {
var Component1 = React.createClass({
getInitialState: function() {
return {.......};
},
componentDidMount: function() {
.......
dates = ....;
Component2.setState({dates: dates});
}
render: function() { return (<div ...></div>) }
});
}
As you can see, I am calling the Component2.setState in this component. But I am getting an error like setState is not a function. I tried this with defining a custom function instead of setState function in component 2 and calling this function from component 1, but I am getting the same error, 'is not a function'.
And component 2:
define([..., ], function(...) {
var Component2 = React.createClass({
getInitialState: function() {
return {.......};
},
render: function() { return (<div ...></div>) }
});
}
So I guess in reactjs we calls a component only for rendering something (React DOM elements)? and cannot change the component state?
If so how can I change a state of a component from a different component which is not a child or parent of first?
Components don't publicly expose their state. It's also important to remember that the state is scoped to the instance of components, not their definition.
To communicate between components, you could roll your own event subscription service.
var events = new EventEmitter();
// inside Component1
events.emit('change-state', newState);
// inside Component2
events.on('change-state', function(state) {
this.setState(state);
});
However, this will quickly become difficult to manage.
A more sensible solution is to use Flux. It allows you to explicitly manage the state of your entire application and subscribe to changes in different bits of the state, within your components. It's worth trying to wrap your head around it.
The component that wants to communicate should dispatch an action and this action will be responsible for changing something in the stores, your other component should subscribe to that store and can update its state based on the change.
You can use a shared state between the two component.
You can build a "mixin" like that
app.mixins.DateMixin = {
getInitialState: function ()
return {
dates: []
}
}
};
and then in you components you can include this mixins using the mixins array
define([..., /path/component_2.jsx], function(..., Component2) {
var Component1 = React.createClass({
mixins: [app.mixins.DateMixin],
getInitialState: function() {
return {.......};
},
componentDidMount: function() {
.......
dates = ....;
this.setState({dates: dates});
}
render: function() { return (<div ...></div>) }
});
}
define([..., ], function(...) {
mixins: [app.mixins.DateMixin],
var Component2 = React.createClass({
getInitialState: function() {
return {.......};
},
render: function() { return (<div ...></div>) }
});
}
Now your components share the "dates" state and you can change/check it in both of them.
NB: you can also share methods with in the same way by writing into a mixin component.
Edit: I suggest you to visit this website http://simblestudios.com/blog/development/react-mixins-by-example.html

ReactJs - uncheck checkbox from postalJs subscription

I have a component I would like to uncheck from a postal.js subscription. Using my code below the checkbox is always checked.
I am storing the value in state and setting it in ComponentDidMount. Can anyone tell me how I can uncheck it once I receive the subscription:
UPDATED:
var SelectAll = React.createClass({
getInitialState: function() {
return {
checked:false
};
},
handler: function(e) {
var updatedContacts = [],
contacts = this.props.data.contacts,
topic = 'selectAll',
checked = false,
channel = 'contact';
contactChannel.publish({
channel: channel,
topic: topic,
data: {
selectAll: this.state.checked
}});
this.setState({checked: event.target.value});
},
render: function() {
return (
<div className="contact-selector">
<input type="checkbox" checked={this.state.checked}
onChange={this.handler} ref="checkAll" />
</div>
);
},
setUnChecked: function(){
this.setState({ selected: false });
},
componentDidMount: function() {
var self = this;
contactChannel.subscribe("deselectedContact", function(data) {
self.setUnChecked();
});
}
});
module.exports = SelectAll;
If you specify the checked attribute to a form field in ReactJS, you set it as controlled. This means, that simple pressing it by a user won't change it's state (or writing sth in case of text fields won't change the text). You need to set it yourself, for example in your handler() function.
P.S. The data flow inside of the component is imho a bit of a mess - according to what I've just wrote, you should use some this.state.XXX variable in the checked={XXX} attribute and, when ckbx's pressed, update it in the handler() function using `this.setState()'. This will trigger automatic rerendering of the component in the DOM (of course if the state changes).
EDIT
<script src="https://fb.me/JSXTransformer-0.13.3.js"></script>
<script src="https://fb.me/react-0.13.3.js"></script>
<div id="container">
</div>
<script type="text/jsx;harmony=true">void function() {
'use strict';
var MyComponent = React.createClass({
getInitialState: function() {
return {
checked: false
};
},
handle: function(e) {
/*
Do your processing here, you might even prevent the checkbox from changing state - actually that is the main purpose of Facebook implementing the controlled/uncontrolled elements thing - to have full control of the input.
*/
var currentMinute = Math.floor(new Date().getTime() / 1000 / 60);
//The checkbox can be changed only in odd minutes
if(currentMinute % 2 == 1) {
this.setState({checked: e.target.checked});
}
},
render: function() {
return <input type="checkbox" checked={this.state.checked} onChange={this.handle} />;
}
});
React.render(<MyComponent />, document.getElementById('container'));
}()</script>
Something like this maybe ?
setChecked: function(isChecked) {
this.setState({
selected: isChecked
});
},
componentDidMount: function() {
var self = this; // <--- storing the reference to "this" so that it can be later used inside nested functions
contactChannel.subscribe("deselectedContact", function(data) {
self.setChecked(false); // <--- "this" would not refer to the react component hence you have to use self
});
}
You can use componentWillReceiveProps(), it is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.

Categories