I'm new to react, I'm curious how to do this properly.
suppose I have this form, by clicking button, I want to get textbox value,
var form = React.createClass({
submit : function(e){
//how to get textbox value?
},
render : function(){
return (
<form>
<input type="text" id="textbox" value="hey, get me"/>
<button onClick={this.submit}>Get it</button>
</form>
);
}
});
Any answer will be appreciated! thank you!
React enforces parent-to-child unidirectional data flow.
So, there's no simple way to access data from siblings.
But, if a child changes the state across the component, you probably want a state for keeping a track of that.
Sample code:
var FormBox = React.createClass({
getInitialState: function () {
return {
textBox: 'hey, get me!'
}
},
pressButton: function () {
alert(this.state.textBox)
},
textChange: function (e) {
this.setState({
textBox: e.target.value
})
},
render: function () {
return (
<form action='.'>
<input type='text' placeholder={this.state.textBox} onChange={this.textChange}/>
<button onClick={this.pressButton}> Get it </button>
</form>
)
}
})
ReactDOM.render(<FormBox />, document.getElementById('root'))
JSBin demo: https://jsbin.com/koxujuwope/edit?js,output
Also, just a suggestion... You should move to ES2015
One way to accomplish this is using refs.
After building your component, you may find yourself wanting to "reach
out" and invoke methods on component instances returned from render(). link to refs docs
Refs are basically the html elements / react instances you want to access. In this case, we want to get Input html element. We get input element by this.inputValue. Then read its value by usual js rule.
var form = React.createClass({
submit : function(e){
e.preventDefault();
console.log(this.inputValue.value);
},
render : function(){
return (
<form onSubmit={this.submit}>
<input type="text" id="textbox" defaultValue="hey, get me"
ref={ function(node){ this.inputValue = node }.bind(this) }
/>
<button onClick={this.submit}>Get it</button>
</form>
);
}
});
here is a jsfiddle for your example, you can check the console for the input value.
Another way to accomplish the same thing is to make your input a controlled element. That means you assign input's value attribute to this.state.inputValue and you pass a onChange event handler as onChange={yourInputHandlerFunction}.
See the answer which explains this way of accomplishing the same thing.
The following code helps me to setup communication between two siblings. The setup is done in their parent during render() and componentDidMount() calls.
It is based on https://reactjs.org/docs/refs-and-the-dom.html
class App extends React.Component<IAppProps, IAppState> {
private _navigationPanel: NavigationPanel;
private _mapPanel: MapPanel;
constructor() {
super();
this.state = {};
}
// `componentDidMount()` is called by ReactJS after `render()`
componentDidMount() {
// Pass _mapPanel to _navigationPanel
// It will allow _navigationPanel to call _mapPanel directly
this._navigationPanel.setMapPanel(this._mapPanel);
}
render() {
return (
<div id="appDiv" style={divStyle}>
// `ref=` helps to get reference to a child during rendering
<NavigationPanel ref={(child) => { this._navigationPanel = child; }} />
<MapPanel ref={(child) => { this._mapPanel = child; }} />
</div>
);
}
}
Related
I'm trying to trigger a click event for my test. As this :
describe('Button', function() {
test('is clicked when player two is pending', (props ={}) => {
const mockRandomAdv = sinon.spy();
const tree = shallow(
<FightButton
button="Random adversary"
isPlayerTwoPending={true}
isPlayerOnePending={false}
onClick={mockRandomAdv}
/>
);
tree.find('Button').simulate('click');
//expect(mockRandomAdv.calledOnce).toEqual(true);
console.log(tree.props().children.onClick)
//.not.toBe('fight-button random');
});
});
The first expectation return false so the click is not triggered.
When i console.log() the click event it returns undefined.
Here is my child (which is not the last last child).
<Button
onClick={ () => { this.props.randomAdversary }}
class="fight-button random"
button="Random adversary"
/>
And here is the parent that is calling the child and who describe the method :
class Board extends Component {
constructor(props) {
..my constructor
}
randomAdversary() {
...my function
}
return (<div> <FightButton
isPlayerTwoPending={this.state.adversaryPending}
isPlayerOnePending={this.state.characterPending}
isPlayerOneTheWinner={this.state.heroWin}
isFighting={this.state.fighting}
randomAdversary={this.randomAdversary}
fight={this.fight(100)}
playAgain={this.playAgain()}
/>
</div>
)
}
When i click, the class of my button must change. But the same when i console.log the class, it has no changed. Is something wrong with my test ?
Looking at the button, I don't see calling the function correctly. I'ts actually doing nothing, you need to execute the function like this:
<Button
onClick={ () => { this.props.randomAdversary() }} // Add the `()`
class="fight-button random"
button="Random adversary"
/>
Have been playing around with react. Have two event listeners the input which listens onChange and the button which should push the value to the array when its clicked.
Here's the code:
import React, { Component } from 'react';
let arr = [];
class App extends Component {
constructor() {
super();
this.state = {text: 'default'}
}
update( e ) {
this.setState({text: e.target.value})
}
add ( value ) {
arr.push(value)
console.log(arr)
}
render() {
return (
<div>
<h1>{this.state.text}</h1>
<input onChange={this.update.bind(this)}/>
<button onClick={this.add(this.state.text)}>Save</button>
</div>
)
}
}
export default App
The problem that the add function is running on change. Can't really get why.
Any suggestions?
onChange() triggers update()
update() calls this.setState() which changes state.
A state change causes render() to be invoked to re-render according to new state.
Rendering <button onClick={this.add(this.state.text)}>Save</button> invokes add() every time render() runs.
In order to defer invoking add(), you can define a function which gets triggered by the click event, as was shown in another answer. Alternatively, you can achieve the same functionality by adding a class method which encapsulates the trigger functionality:
addText() {
this.add(this.state.text)
}
render() {
…
<button onClick={this.addText.bind(this)}>Save</button>
This may or may not work for you, but in the context of the example, given, this would work.
Change <button onClick={this.add(this.state.text)}>Save</button>
To <button onClick={() => this.add(this.state.text)}>Save</button>
In your variant function add firing when component is rendering, and when you call setState with onChange of input you call this re-render.
The problem is add(this.state.text) is called whenever render() is called. To avoid this, you do not need to send the state as parameter, all you need to do is
<button onClick={this.add}>Save</button
or if you want to send a parameter you should bind it
<button onClick={this.add.bind(this, this.state.text)}>Save</button>
When handling events in a (dump) child component in React, what should be supplied to the callback passed from its (smart) parent component to make it as intended? Should it be the event or only the portion of the result that we are interested in? How does it scale when we have deeply nested components? Are there some other considerations?
Intuitively, I see benefits behind passing the whole event because (i) we can get more data from the event when handling it in the parent and (ii) it separates concerns (the dump components only render and have no logic). On the other hand, it requires the child to have a constructor to bind the wrapper method.
I've seen both approaches used. For example, in Thinking in React the author wraps callbacks in the child component to pass values (see the code on CodePen), whereas in most of the SO posts the event is passed and its value is extracted in the parent component via event.target.value.
Code examples
Pass event:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
this.setState({checked: event.target.checked});
}
render() {
return (
<Child checked={this.state.checked} handleClick={this.handleClick}/>
);
}
}
class Child extends React.Component {
render() {
return (
<p>
<input
type="checkbox"
checked={this.props.checked}
onChange={this.props.handleClick}
/>
{" "}
Click me
</p>
);
}
}
Pass value only (notice handleClick2 ):
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(checked) {
this.setState({checked: checked});
}
render() {
return (
<Child checked={this.state.checked} handleClick={this.handleClick}/>
);
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.handleClick2 = this.handleClick2.bind(this);
}
handleClick2(event) {
this.props.handleClick(event.target.checked);
}
render() {
return (
<p>
<input
type="checkbox"
checked={this.props.checked}
onChange={this.handleClick2}
/>
{" "}
Click me
</p>
);
}
}
You should pass the thing that you need without the event. There is no need for the whole object unless you want to extract relevant data from the event: for example the target or when you use the same callback for multiple elements/actions.
You won't have any performance problems and there is definitely no react-ish way to do this. Just use your judgement.
event.target is part of the Web Platform standard. For example:
Lets look at an example of how events work in a tree:
<!doctype html>
<html>
<head>
<title>Boring example</title>
</head>
<body>
<p>Hello <span id=x>world</span>!</p>
<script>
function debug(target, currentTarget, eventPhase)
{
console.log("target: " + JSON.stringify(target) );
console.log("currentTarget: " + JSON.stringify(currentTarget) );
console.log("eventPhase: " + JSON.stringify(eventPhase) );
}
function test(e) {
debug(e.target, e.currentTarget, e.eventPhase)
}
document.addEventListener("hey", test, true)
document.body.addEventListener("hey", test)
var ev = new Event("hey", {bubbles:true})
document.getElementById("x").dispatchEvent(ev)
</script>
</body>
</html>
The debug function will be invoked twice. Each time the events's target attribute value will be the span element. The first time currentTarget attribute's value will be the document, the second time the body element. eventPhase attribute's value switches from CAPTURING_PHASE to BUBBLING_PHASE. If an event listener was registered for the span element, eventPhase attribute's value would have been AT_TARGET.
So it would be easy to port to something newer or renewable.
References
W3C DOM4
Accelerated Mobile Pages – A new approach to web performance
Event Handling — Vue.js
AMP Actions and Events: ampproject/amphtml
TypeScript: Using jquery $(this) in event
i wanna edit my p element's text when i press edit button.
When i click edit button, i display a textarea and get keyed text with alert but can't put that text to my p element.
What is the simple way to do it with React.js ?
JSFIDDLE
When i press edit button, editing state changing and textarea shows up.The code below.
renderNormal: function() {
return (
<div>
<p>Edit me</p>
<button onClick={this.edit}>Edit</button>
</div>
)
},
renderForm: function() {
return (
<div>
<textarea ref="newText" defaultValue="Edit me"></textarea>
<button onClick={this.save}>Save</button>
</div>
)
},
render: function() {
if (this.state.editing) {
return this.renderForm()
} else {
return this.renderNormal()
}
}
You need to store and retrieve the text from a state variable. Modifying the state causes a re-render, which will then display the updated text. Copied from your JSFiddle... note where I've added a "text" property to your state
var MyCom = React.createClass({
getInitialState: function() {
return {
editing: false,
// ** Initialize "text" property with empty string here
text: ''
}
},
edit: function() {
this.setState({
editing: true
})
},
save: function() {
var val = this.refs.newText.value;
alert(val)
this.setState({
// ** Update "text" property with new value (this fires render() again)
text: val,
editing: false
})
},
renderNormal: function() {
// ** Render "state.text" inside your <p> whether its empty or not...
return (
<div>
<p>{this.state.text}</p>
<button onClick={this.edit}>Edit</button>
</div>
)
},
renderForm: function() {
return (
<div>
<textarea ref="newText" defaultValue="Edit me"></textarea>
<button onClick={this.save}>Save</button>
</div>
)
},
render: function() {
if (this.state.editing) {
return this.renderForm()
} else {
return this.renderNormal()
}
}
})
ReactDOM.render(
<div>
<MyCom/>
<MyCom/>
</div>,
document.querySelector(".box")
)
You have to set a value on the state on the button is clicked, and then read that on render.
Example:
handler : function () {
this.setState({ value : 'hey' });
},
render : function () {
return <p>{ this.state && this.state.value ? this.state.value : 'hoy' }</p>;
}
Everytime you want the render method to change according to something that can happen, the action that triggers it must trigger a state change, and change the state object, and the object will be rerendered, so the render method should check the current state.
If you want to change the value of a input (or in your case, a textarea), you can use the linkedstate pattern as a two way databinding (more here).
I particularly use this lib. There are plenty of examples there.
I actually had this same problem, here was my solution:
{editableSubTasks.filter(id => id === subTask.Id).length > 0 ? <input type="text" /> : <span>{subTask.Name}</span>}
So basically, you have an array, in my case it was editableSubTasks. Whenever I would trigger the text element to change to a textarea, I would simply add that guy to the edtiableSubTasks array. And then in my map function, if the Id of the correspnding item was in the editableSubTasks array, I would use a ternary operator to tell it to be a <input type="text">, otherwise just a span. In your case obviously you can use a textarea. This worked out wonderfully. Here was my SO with the answer that I ended up using:
How to dynamically show/hide a list of items in react
If you don't need the array, then the answer is even simpler, just have a bool and change it to true when you want it to be a text area.
var B = React.createClass({
render: function() {
return (
<div>
<input type="button" onClick={this.props.saveFunction} />
</div>
);
}
});
var A = React.createClass({
render: function() {
return (
<div>
<B saveFunction={this.save} />
</div>
);
},
save: function(){
//Save code
}
});
Hello,
I am working in facebook react js.
I have created two components wherein A is parent component and B is child component.
I have written Save function in A and passing its reference as "props" to component B.
I am passing this Save function reference to "onClick" event of button rendered from B and I also want to save the values when user press the ENTER button.
So, I am trying to pass the same reference to "onKeyDown" event of same button.
But, its not working.
Contstraint is: I can't shift save function from A to B.
Please let me know how I can achieve this.
Thanks in advance!
I you pass this.save function to child element it loses it's context (JavaScript tires to execute save function when keyword this points to B element).
Bind that function with .bind(this) like:
<B saveFunction={this.save.bind(this)} />