React JS reference function in another component - javascript

I'm trying to get a button rendered through another component to reference and/or influence the state of a different component.
var Inputs = React.createClass({
getInitialState: function(){
return {count: 1};
},
add: function(){
this.setState({
count: this.state.count + 1
});
},
render: function(){
var items = [];
var inputs;
for (var i = 0; i < this.state.count; i++){
items.push(<input type="text" name={[i]} />);
items.push(<br />);
}
return (
<div className="col-md-9">
<form action="/" method="post" name="form1">
{items}
<input type="submit" className="btn btn-success" value="Submit Form" />
</form>
</div>
);
}
});
I want to write a new component that will be able to access the add function in Inputs. I tried to reference it directly with Inputs.add like this:
var Add = React.createClass({
render: function(){
return (
<input type="button" className="btn" value="Add an Input" onClick={Inputs.add} />
);
}
});
But that didn't work. How would I be able to access a component's functions through another component, or influence the state of a component through another component? Thanks.

You could accomplish this by creating a parent component that is responsible for managing the state and then just push the state down to the sub-components as props.
/** #jsx React.DOM */
var Inputs = React.createClass({
render: function () {
var items = [];
var inputs;
for (var i = 0; i < this.props.count; i++) {
items.push( <input type="text" name={[i]} />);
items.push(<br />);
}
return (
<div className = "col-md-9">
<form action = "/" method = "post" name = "form1">
{items}
<input type="submit" className="btn btn-success" value = "Submit Form" />
</form>
</div>
);
}
});
var Add = React.createClass({
render: function () {
return (<input type = "button" className="btn" value="Add an Input" onClick={this.props.fnClick}/> );
}
});
var Parent = React.createClass({
getInitialState: function(){
return {count:1}
},
addInput: function(){
var newCount = this.state.count + 1;
this.setState({count: newCount});
},
render: function(){
return (
<div>
<Inputs count={this.state.count}></Inputs>
<Add fnClick={this.addInput}/>
</div>
);
}
});
React.renderComponent(<Parent></Parent> , document.body);
jsFiddle

You can call functions on the return value of renderComponent:
var Inputs = React.createClass({…});
var myInputs = React.renderComponent(Inputs);
myInputs.add();
The only way to get a handle to a React Component instance outside of React is by storing the return value of React.renderComponent. Source.

Related

Component didn't re-renders on state change?

I am creating a random quote generator. There is a quote box that displays quote and author names. I created a method to invoke on a button click that may randomize the quote list and display a new quote and a next button to get next quote from my quote list. I can see the first quote but the component didn't re-renders on clicking buttons or something gets wrong that I can't get next quote or can't randomize. Here is the code:
class UI_qoutebox extends React.Component {
constructor(props) {
super(props);
this.state = { qoutes: props.qoutes, authors: props.authors, num: 0 };
this.UI_rq = this.UI_rq.bind(this);
this.UI_next = this.UI_next.bind(this);
}
UI_rq() {
let rnd = Math.floor(Math.random() * this.state.qoutes.length);
this.setState({ num: rnd });
}
UI_next() {
let p = this.state.num + 1;
if (p > this.state.qoutes.length) { p = 0 }
this.setState({ num: p })
}
render() {
const { qoutes, authors, num } = this.state;
return (
<div className="qoute-box">
<span className="qoute">{qoutes[num]}</span>
<span>{authors[num]}</span>
<input type="button" value="Randomize" onClick={() => this.UI_rq} />
<input type="button" value="Next" onClick={() => this.UI_next} />
</div>
)
}
}
I am working on Freecodecamp's project and I need quick help. Thanks in advance.
Change this
<input type="button" value="Randomize" onClick={this.UI_rq}/>
<input type="button" value="Next" onClick={this.UI_next}/>

set state in onClick function

I have 2 submit buttons in my react form.
I am trying to use an onClick function to get the id of the clicked button, so I can then specify how to handle each accordingly.
My onClick function is returning undefined for the setState of the id.
How can I properly grab the id of the button and set the state?
handleClick() {
var buttons = document.getElementsByTagName("button");
var buttonsCount = buttons.length;
for (var i = 0; i < buttonsCount; i++) {
buttons[i].onclick = (e) => {
this.setState({clickedSubmit: this.id});
console.log(this.state.clickedSubmit); //returns undefined
};
}
}
//in the render
<button id="formSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick}>Submit</button>
<button id="hashSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick}>Generate Hash</button>
You will find your element id in the event of the onClick handler that is e.target.id
handleClick = (e) => {
this.setState({ clickedSubmit: e.target.id },() => {
console.log(this.state.clickedSubmit)
});
}
//in the render
<button id="formSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick}>Submit</button>
<button id="hashSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick}>Generate Hash</button>
I recommend changing how you invoke handleClick slightly, by passing in an argument for buttonId.
In your render function:
<button onClick={() => this.handleClick('formSubmit')}>Submit</button>
In handleClick:
handleClick(buttonId) { ... }
As I posted in my comment, you have an option to separate the button out into it's own component. In that case, you would be able to use simple like this.props.id to get the value of id.
I would suggest you to go for a simpler approach, something like this
handleClick1() {
var buttons = document.getElementsByTagName("button");
var buttonsCount = buttons.length;
let id = 1;
for (var i = 0; i < buttonsCount; i++) {
buttons[i].onclick = (e) => {
this.setState({clickedSubmit: id});
console.log(this.state.clickedSubmit); //returns undefined
};
}
}
handleClick2() {
var buttons = document.getElementsByTagName("button");
var buttonsCount = buttons.length;
let id = 2;
for (var i = 0; i < buttonsCount; i++) {
buttons[i].onclick = (e) => {
this.setState({clickedSubmit: id});
console.log(this.state.clickedSubmit); //returns undefined
};
}
}
//in the render
<button id="formSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick1}>Submit</button>
<button id="hashSubmit" className="btn btn-info" name="submitButton" onClick={this.handleClick2}>Generate Hash</button>
The benefit of this approach is, in future when your button count increases, then at that time, your code should be modular so that it is easy to add new functionality to your app.
Hope this helps!
You can get id of your buttons by using event.target, like below:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
clickedId: -1,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
const id = event.target.id;
this.setState({
clickedId: id,
});
}
render() {
return (
<div>
<h1>Clicked id: {this.state.clickedId}</h1>
<button id="asd" onClick={this.handleClick}>Asd</button>
<button id="qwe" onClick={this.handleClick}>Qwe</button>
</div>
);
}
}
DEMO: http://jsbin.com/yiqavav/1/edit?html,js,output
You can try this, even change id to other parameter you want to pass in to the your function.
<button onClick={(e) => this.handlefunction(id, e)}>Delete Row</button>
<button onClick={this.handlefunction.bind(this, id)}>Delete Row</button>
This article helps a great deal:
https://reactjs.org/docs/handling-events.html

uncheck checkbox programmatically in reactjs

I am messing with checkboxes and I want to know that is there a way in which I can uncheck a checkbox on click of a button by calling a function?? If so? How can I do that?
<input type="checkbox" className="checkbox"/>
<button onClick={()=>this.unCheck()}
How can I uncheck the checkbox programmatically and what if I have multiple checkboxes generated dynamically using map function.
How can I uncheck them If I want to?
There is property of checkbox checked you can use that to toggle the status of check box.
Possible Ways:
1- You can use ref with check boxes, and onClick of button, by using ref you can unCheck the box.
2- You can use controlled element, means store the status of check box inside a state variable and update that when button clicked.
Check this example by using ref, assign a unique ref to each check box then pass the index of that item inside onClick function, use that index to toggle specific checkBox:
class App extends React.Component{
constructor(){
super();
this.state = {value: ''}
}
unCheck(i){
let ref = 'ref_' + i;
this.refs[ref].checked = !this.refs[ref].checked;
}
render(){
return (
<div>
{[1,2,3,4,5].map((item,i) => {
return (
<div>
<input type="checkbox" checked={true} ref={'ref_' + i}/>
<button onClick={()=>this.unCheck(i)}>Toggle</button>
</div>
)
})}
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<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='app'/>
Check this example of controlled element, means storing the state of checkbox inside state variable, and on click of button change the value of that:
class App extends React.Component{
constructor(){
super();
this.state = {value: []}
}
onChange(e, i){
let value = this.state.value.slice();
value[i] = e.target.checked;
this.setState({value})
}
unCheck(i){
let value = this.state.value.slice();
value[i] = !value[i];
this.setState({value})
}
render(){
return (
<div>
{[1,2,3,4,5].map((item,i) => {
return (
<div>
<input checked={this.state.value[i]} type="checkbox" onChange={(e) => this.onChange(e, i)}/>
<button onClick={()=>this.unCheck(i)}>Toggle</button>
</div>
)
})}
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<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='app'/>
React
Checked
Using State
<input type="radio" name="count" value="minus" onChange={this.handleRadioChange} checked={this.state.operation == "minus"} /> Decrement
2.Using Refs
<input type="radio" name="count" ref="minus" /> Decrement
onSubmit(e){ this.refs.minus.checked = false }
Using plain javascript you can acheive like below.
function unCheck() {
var x = document.getElementsByClassName("checkbox");
for(i=0; i<=x.length; i++) {
x[i].checked = false;
}
}
Small DEMO
I was thinking of a thing like that:
<input onChange={(input) => this.onFilterChange(input)} className="form-check-input" type="checkbox" />
onFilterChange = (input) => { let { value, checked } = input.target;}
unCkeckAll = () => {
[...document.querySelectorAll('.form-check-input')].map((input) => {
if (input.checked) {
let fakeInput = {
target: {
value: input.value,
checked: false
}
}
input.checked = !input.checked;
this.onFilterChange(fakeInput);
}
return null;
})
}
Checkboxes have a checked property, you can hook it to the state and change it dynamically. Check these links:
https://facebook.github.io/react/docs/forms.html#handling-multiple-inputs
http://react.tips/checkboxes-in-react/
Sometime its good to use plain javascript. If you have of checkbox value in any of your state then try this
let checkboxValue = xyz
document.querySelectorAll("input[value="+checkboxValue+"]")[0].checked = false;

Angular ng-model not updating after typing into number input

So basically I'm creating a simple app with two controllers. ControllerA button increments ControllerB number input and vicer versa.
The problem is that $scope.total is not updating after typing into number input manually, and I don't know what would be the best way to achieve this.
HTML
<div ng-app="tabsApp">
<div id="tabOne" class="tabcontent">
<div ng-controller="tabOneController as vm">
<input type="button" value="increment value in tab 2" ng-click="vm.sumar()"/>
<input type="number" ng-model="vm.totalB.value">
</div>
</div>
<div id="tabTwo" class="tabcontent">
<div ng-controller="tabTwoController as vm">
<input type="button" value="increment value in tab 1" ng-click="vm.sumar()"/>
<input type="number" ng-model="vm.total.value">
</div>
</div>
</div>
JS
var app = angular.module('tabsApp', []);
app.controller("tabOneController", controllerA);
app.controller("tabTwoController", controllerB);
app.service('myData', function() {
var data = {
value: 0
}, dataB = {
value: 0
};
this.addItem = function (value) {
data.value = value;
}
this.getItem = function() {
return data;
}
this.addItemB = function (value) {
dataB.value = value;
}
this.getItemB = function() {
return dataB;
}
});
function controllerA(myData){
var scope = this;
scope.total = 0;
scope.sumar = function(){
scope.total++;
myData.addItem(scope.total);
}
scope.totalB = myData.getItemB();
}
function controllerB(myData){
var scope = this;
scope.totalB = 0;
scope.sumar = function(){
scope.totalB = myData
scope.totalB++;
myData.addItemB(scope.totalB);
}
scope.total = myData.getItem();
}
Here's a working example based on your code : Plunker
function controllerA(myData){
var scope = this;
scope.total = 0;
scope.sumar = function(){
scope.total = myData.getItem().value; // added this line
scope.total++;
myData.addItem(scope.total);
}
scope.totalB = myData.getItemB();
}
function controllerB(myData){
var scope = this;
scope.totalB = 0;
scope.sumar = function(){
scope.totalB = myData.getItemB().value; // modified this line
scope.totalB++;
myData.addItemB(scope.totalB);
}
scope.total = myData.getItem();
}
scope.totalB = myData.getItemB(); // first controller
scope.total = myData.getItem(); // second controller
These will be called just once when controller is loaded. Place them inside the function sumar
Use vm.total and vm.totalB instead of vm.total.value and vm.totalB.value in html
You could try implementing required ng-change="controller.functionThatIncrementsValues" in your html.
Would something like this help:
HTML
<div ng-app="tabsApp" ng-controller="tabController as vm">
<div id="tabOne" class="tabcontent">
<div>
<input type="button" ng-click="vm.one++" />
<input type="number" ng-model="vm.two">
</div>
</div>
<div id="tabTwo" class="tabcontent">
<div>
<input type="button" ng-click="vm.two++" />
<input type="number" ng-model="vm.one">
</div>
</div>
<p>Total (method 1): {{vm.one + vm.two}}</p>
<p>Total (method 2): {{ total(vm.one, vm.two) }}</p>
</div>
JS
var app = angular.module('tabsApp', []);
app.controller("tabController", function() {
this.one = 0;
this.two = 0;
this.total = function(one, two) {
return one + two;
}
})
Unless you have a specific need for two controllers and a service I would just put this all in one controller. At the moment what you have is massive overkill.

push increment item into an array in Angularjs

http://plnkr.co/edit/NDTgTaTO1xT7bLS1FALN?p=preview
<button ng-click="addRow()">add row</button>
<div ng-repeat="row in rows">
<input type="text" placeholder="name"><input type="tel" placeholder="tel">
</div>
I want to push new row and save all the fields but now I'm stuck at adding new rows. How to know the current number of row and do increment to push into the array?
Look at this example I created which allows you to generate up to eight unique input fields for Telephone and Text Entries.
var app = angular.module("MyApp", []);
app.controller("MyCtrl", function($scope) {
$scope.rows = [];
var Row = function(tel, text) {
// Private data
var private = {
tel: tel,
text: text
}
// Expose public API
return {
get: function( prop ) {
if ( private.hasOwnProperty( prop ) ) {
return private[ prop ];
}
}
}
};
$scope.addRow = function(){
if($scope.rows.length < 8){
var newItemNum = $scope.rows.length + 1;
var row = new Row('item' + newItemNum, 'item' + newItemNum);
$scope.rows.push(row);
}
};
$scope.saveAll = function(){
// $scope.result = 'something';
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="MyCtrl">
<h2>Setting</h2>
<button ng-click="addRow()">Add Row</button>
<br />
<div ng-repeat="row in rows">
<input type="text" placeholder="Text" ng-model="row.textModel" >
<input type="tel" placeholder="Phone" ng-model="row.telModel" >
</div>
<br />
{{rows}}
</div>
</div>
Move functions inside controller 'Ctrl'.
In your script:
function Ctrl($scope) {
$scope.result = "something";
$scope.rows = ['1'];
$scope.addRow = function(){
if ($scope.rows.length < 8) {
$scope.rows.push($scope.rows.length + 1);
}
}
$scope.saveAll = function(){
// $scope.result = 'something';
}
}

Categories

Resources