I am pretty new to React. I am trying to create a simple form and pass values into an 'onclick' handler. You can see the code below:
const reactContainer = document.getElementById('react');
let SForm = React.createClass({
getApps: function(){
getAppsExternal(document.getElementsByClassName("token")[0].value,document.getElementsByClassName("publisher_id")[0].value)
},
render: function(){
return (
React.createElement("div",{className: "container"},"",
React.createElement("div",{},"Authentication Token: ","",
React.createElement("input",{type: "password",className:"token",maxLength:"30"})),
React.createElement("div",{},"Publisher ID: ",
React.createElement("input",{type: "text",className:"publisher_id",maxLength:"7"})),
React.createElement("button",{className:"get_apps_button",onClick:this.getApps},"Get Apps"))
)
}
})
let elementTester =React.createElement(SForm)
ReactDOM.render(React.createElement(SForm),reactContainer)
My question is, how do I pass the parameters into getAppsExternal the 'react' way without using document.getElementsByClassName ?
See: https://reactjs.org/docs/forwarding-refs.html
Assuming you use the lattest React, you can use React.createRef()
const reactContainer = document.getElementById('react');
let SForm = React.createClass({
componentWillMount: function() {
this.tokenRef = React.createRef()
this.publisherRef = React.createRef()
},
getApps: function(){
getAppsExternal(this.tokenRef.current.value, this.publisherRef.current.value)
},
render: function(){
return (
React.createElement("div",{className: "container"},"",
React.createElement("div",{},"Authentication Token: ","",
React.createElement("input",{type: "password",className:"token",maxLength:"30", ref: this.tokenRef})),
React.createElement("div",{},"Publisher ID: ",
React.createElement("input",{type: "text",className:"publisher_id",maxLength:"7", ref: this.publisherRef})),
React.createElement("button",{className:"get_apps_button",onClick:this.getApps},"Get Apps"))
)
}
})
let elementTester =React.createElement(SForm)
ReactDOM.render(React.createElement(SForm),reactContainer)
If it's not available for you, there is the callback approach
const reactContainer = document.getElementById('react');
let SForm = React.createClass({
setTokenRef: function(ref) {
this.tokenRef = ref
},
setPublisherRef: function(ref) {
this.publisherRef = ref
},
getApps: function(){
getAppsExternal(this.tokenRef.value, this.publisherRef.value)
},
render: function(){
return (
React.createElement("div",{className: "container"},"",
React.createElement("div",{},"Authentication Token: ","",
React.createElement("input",{type: "password",className:"token",maxLength:"30", ref: this.setTokenRef.bind(this)})),
React.createElement("div",{},"Publisher ID: ",
React.createElement("input",{type: "text",className:"publisher_id",maxLength:"7", ref: this.setPublisherRef.bind(this)})),
React.createElement("button",{className:"get_apps_button",onClick:this.getApps.bind(this)},"Get Apps"))
)
}
})
let elementTester =React.createElement(SForm)
ReactDOM.render(React.createElement(SForm),reactContainer)
As you're not using arrow functions, don't forget to bind your callbacks like above
The simplest way, you can create a getInitialState then make a onChange function that will set the values to the state then you will be able to use them like this {this.state.password}
getInitialState: function() {
return {password: '', publisher: ''};
},
onChange: function(e){
this.setState({ [e.target.name]: e.target.value });
},
render: function(){
return (
React.createElement("div",{className: "container"},"",
React.createElement("div",{},"Authentication Token: {this.state.password}","",
React.createElement("input",{type: "password",className:"token",maxLength:"30",name: 'password',value: this.state.password,onChange: this.onChange.bind(this)})),
React.createElement("div",{},"Publisher ID: {this.state.publisher} ",
React.createElement("input",{name: 'publisher',type: "text",className:"publisher_id",maxLength:"7",value: this.state.publisher, onChange: this.onChange.bind(this)})),
React.createElement("button",{className:"get_apps_button",onClick:this.getApps},"Get Apps"))
)
}
Related
I have a problem to find a way to easy get state value from component in other component. When I click on any tag.
I want to append value of tag to list state in Form component if tag is clicked. Is there any simply way to do this ?
I have component like this:
var Tag = React.createClass({
getInitialState: function(){
return {
checked: false
};
},
componentDidMount: function() {
this.setState({
checked: false
});
},
_onChange: function(event) {
if(this.state.checked == false){
this.setState({
checked: true
});
} else {
this.setState({
checked: false
});
}
},
render: function() {
return (
<div className="review-tag">
<input
type="checkbox"
id={this.props.id}
name={this.props.name}
checked={this.state.checked}
value={this.props.id}/>
<label htmlFor={this.props.name} onClick={this._onChange}>{this.props.name}</label>
</div>
);
return (
<div>
<span>{this.props.name}</span>
</div>
);
}
});
var allTags = tags;
var ReviewTag = React.createClass({
render: function() {
const tagComps = allTags.map(function(tag){
return <Tag {...tag}/>;
});
return (
<div>
{tagComps}
</div>
);
}
});
Tag component have included value at rendering and I have to get value of each tag in my ReviewForm component which looks like this:
var fd = new FormData();
var ReviewForm = React.createClass({
getInitialState: function(){
return {
Author: '',
Tags: ''
};
},
componentDidMount: function() {
this.setState({
Author: author,
Tags: tags
});
},
submit: function (e){
var self;
e.preventDefault();
self = this;
var data = {
tags: this.state.Tags,
author: this.state.Author
};
for (var key in data) {
fd.append(key, data[key]);
}
$.ajax({
type: 'POST',
url: '/reviews/submit/',
data: fd,
processData: false,
contentType: false
})
.done(function(data) {
console.log('Review added successfully.');
})
.error(function(msg) {
var errors = msg.responseJSON;
console.log(errors);
});
},
render: function() {
<div className="scolumn">
<ReviewTag/>
</div>
}
})
Many thanks for any help.
You don't need to set state in componentDidMount — remove that piece of code.
Really, couldn't all of that...
_onChange: function(event) {
if(this.state.checked == false){
this.setState({
checked: true
});
} else {
this.setState({
checked: false
});
}
},
be substituted with _onChange: function(event) { this.setState(checked: !this.state.cheked) }?
allTags should be passed as property, not as outer scope variable.
Now to your main question.
Move state to parent component (ReviewForm). You can have state as a hash with {name: isChecked} structure (name is Tag's name, and isChecked is boolean). Pass it down to ReviewTag and futher down to Tag as property. Also pass down your _onChange handler as property (it should be moved to parent component — ReviewForm — as well).
So when the Tag is checked, it calls ReviewForm's handler, and ReviewForm's handler changes its own state accordingly.
I am new to React and javascript and trying to use moroshko's component react-autosuggest, but failing to get event handlers to bind correctly. I'm writing in coffeescript, but will paste compiled javascript too.
define [
'jquery',
'react',
'reactdom',
'autosuggest'
], (jQuery, React, ReactDOM, Autosuggest) ->
escapeRegexCharacters = (str) ->
str.replace /[.*+?^${}()|[\]\\]/g, '\\$&'
getSuggestions = (praxes, value) ->
if value == ""
return []
val = escapeRegexCharacters(value.trim())
regex = new RegExp('^' + val, 'i')
praxes.filter((prax) => regex.test(prax.species))
getPraxSpecies = (prax) ->
prax.species
renderSpecies = (prax) ->
React.createElement("span", null, getPraxSpecies(prax))
Species = React.createClass
getInitialState: ->
value: ''
suggestions: getSuggestions(#props.praxes, '')
onChange: (event, {newValue}) ->
#setState({value: newValue})
onSuggestionsUpdateRequested: ({value}) ->
#setState {suggestions: getSuggestions(#props.praxes, value)}
render: ->
inputProps =
placeholder: "Choose a species"
value: ''
onChange: #onChange
autosuggest = React.createFactory Autosuggest
React.DOM.div {key: 'autosugg', className: 'praxis'},
autosuggest {
key: 'autoSp',
suggestions: #state.suggestions,
onSuggestionsUpdateRequested: #onSuggestionsUpdateRequested,
getSuggestionValue: getPraxSpecies,
renderSuggestion: renderSpecies,
inputProps: inputProps
}
Species
or coffeescript above compiled to javascript below:
(function() {
define(['jquery', 'react', 'reactdom', 'autosuggest'], function(jQuery, React, ReactDOM, Autosuggest) {
var Species, escapeRegexCharacters, getPraxSpecies, getSuggestions, renderSpecies;
escapeRegexCharacters = function(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};
getSuggestions = function(praxes, value) {
var regex, val;
if (value === "") {
return [];
}
val = escapeRegexCharacters(value.trim());
regex = new RegExp('^' + val, 'i');
return praxes.filter((function(_this) {
return function(prax) {
return regex.test(prax.species);
};
})(this));
};
getPraxSpecies = function(prax) {
return prax.species;
};
renderSpecies = function(prax) {
return React.createElement("span", null, getPraxSpecies(prax));
};
return Species = React.createClass({
getInitialState: function() {
return {
value: '',
suggestions: getSuggestions(this.props.praxes, '')
};
},
onChange: function(event, _arg) {
var newValue;
newValue = _arg.newValue;
return this.setState({
value: newValue
});
},
onSuggestionsUpdateRequested: function(_arg) {
var value;
value = _arg.value;
return this.setState({
suggestions: getSuggestions(this.props.praxes, value)
});
},
render: function() {
var autosuggest, inputProps;
inputProps = {
placeholder: "Choose a species",
value: '',
onChange: this.onChange
};
autosuggest = React.createFactory(Autosuggest);
console.log('this: ' + this);
return React.DOM.div({
key: 'autosugg',
className: 'praxis'
}, autosuggest({
key: 'autoSp',
suggestions: this.state.suggestions,
onSuggestionsUpdateRequested: this.onSuggestionsUpdateRequested,
getSuggestionValue: getPraxSpecies,
renderSuggestion: renderSpecies,
inputProps: inputProps
}));
}
}, Species);
});
}).call(this);
The autosuggest component displays properly initially, accepts input, and calls onChange and onSuggestionsUpdateRequested functions in that order. State is updated, but possibly not attached to the correct component. The autosuggest then repaints exactly as initially (i.e. value='').
Substituting fat-arrow => for -> in defining onChange results in an error: Uncaught TypeError: _this.setState is not a function
I have also tried using the coding style of moroshko's example without success. Clearly, I am missing something ...
I think the problem is in your inputProps. You need to use this.state.value:
inputProps =
placeholder: "Choose a species"
value: ''
onChange: #onChange
to:
inputProps =
placeholder: "Choose a species"
value: #state.value
onChange: #onChange
I've got a parent component that has 2 child components;
UPDATE
I've rewritten some statements and code to make it more understandable.
Parent: ReservationFormComponent
Children: ReservationTypePanel & ReservationCalendarPanel
The parent component ReservationFormComponent initially displays the ReservationTypePanel only. The other sibling ReservationCalendarPanel is hidden until an item is selected on ReservationTypePanel.
So the problem is when an item is selected in ReservationTypePanel the ReservationCalendarPanel is rendered with initial values set in the ReservationFormStore store. Particularly
initialize: function(){
this.reservationType = void 8;
this.pickupTime = moment().add('minutes',30);
}
So when the ReservationCalendarPanel is rendered, its child Component DateTimeField which accepts the state pickupTime get re-rendered and fires up the onChange event which calls for another action
return DateTimeField({
pickupTime: pickupTime,
onChange: function(time){
// Here is where the action gets called again
this$.getFlux().actions.setReservationPickupTime(time);
}
});
And greets me with this error Uncaught Error: Cannot dispatch an action while another action is being dispatched
I've tried my best to trim down the codes below. I wasn't using JSX because the original code was in LiveScript so I just took the compiled code to display here instead.
This is the parent component ReservationFormComponent
ReservationFormComponent = React.createClass({
get flux(){ // Instantiating Fluxxor
return new Fluxxor.Flux({ // These are the stores
'reservation-form': new ReservationFormStore,
'reservation-types': new ReservationTypeStore
}, { // These are the actions
setReservationType: function(value){
return this.dispatch('SET_RESERVATION_TYPE', value);
},
setReservationPickupTime: function(value){
return this.dispatch('SET_RESERVATION_PICKUP_TIME', value);
}
});
},
componentWillMount: function(){
this.flux.store('reservation-form').addListener('change', this.onChange);
},
onChange: function(){ // This triggers the re-render to display the ReservationCalendarPanel
this.setState({
pickupTime: this.flux.store('reservation-form').pickupTime
});
},
render: function() {
reservationType = this.state.reservationType;
return form({
className: 'container'
}, ReservationTypePanel({
flux: this.flux
}), reservationType ? ReservationCalendarPanel({
flux: this.flux
}) : null // This conditional to mount or not mount the component
);
}
});
The ReservationTypePanel Component. Here, the rendered component listens to onClick event and dispatches setReservationType action.
ReservationTypePanel = React.createClass({
mixins: [fluxxor.FluxMixin(react)],
onSelectReservationType: function(reservationType){
var this$ = this;
return function(event){
this$.getFlux().actions.setReservationType(reservationType);
};
},
render: function() {
var this$ = this;
return ReservationTypeItem({
onClick: this$.onSelectReservationType(type);
})
}
});
The ReservationCalendarPanel Component. Here is where the DateTimeField is rendered and receives the state from the ReservationFormStore and sets the value which causes another dispatch. This is where the error comes.
ReservationCalendarPanel = React.createClass({
mixins: [fluxxor.FluxMixin(react)],
getInitialState: function() {
return {pickupTime: moment()} // sets the current time
},
componentWillMount: function(){
this.getFlux().store('reservation-form').addListener('change-pickup-time', this.onFlux);
},
componentWillUnmount: function(){
this.getFlux().store('reservation-form').removeListener('change-pickup-time', this.onFlux);
},
render: function() {
var this$ = this;
if (this.state.pickupTime) {
pickupTime = moment(this.state.pickupTime);
}
return DateTimeField({
date: pickupTime,
onChange: function(time){
// Here is where the action gets called again
this$.getFlux().actions.setReservationPickupTime(time);
}
});
});
This is the DateTimeField this is where the
DateTimeField = React.createClass({
getInitialState: function(){
return {
text: ''
};
},
componentWillReceiveProps: function(nextProps){
this.setDate(nextProps.date);
},
componentDidMount: function(){
$(this.getDOMNode()).datepicker()
.on('changeDate', this.onChangeDate)
.on('clearDate', this.onChangeDate);
this.setDate(this.props.date);
},
componentWillUnmount: function(){
return $(this.getDOMNode()).datepicker('remove');
},
getDatepickerDate: function(){
return $(this.getDOMNode()).datepicker('getDate');
},
setDate: function(date){
if (!this.isMounted()) {
return;
}
if (moment(date).isSame(this.getDatepickerDate, 'day')) {
// If there is no change between the date that
// is about to be set then just ignore and
// keep the old one.
return;
}
date = date ? moment(date).toDate() : void 8;
$(this.getDOMNode()).datepicker('setDate', date);
},
onChangeDate: function(event){
if (this.props.onChange) {
this.props.onChange(event.date);
}
},
render: function(){
return this.transferPropsTo(input({
type: 'text',
className: 'form-control'
}));
}
});
If in case here is the store:
ReservationFormStore = fluxxor.createStore({
actions: {
SET_RESERVATION_TYPE: 'setReservationType',
SET_RESERVATION_PICKUP_TIME: 'setPickupTime'
},
initialize: function(){
this.reservationType = void 8;
this.pickupTime = moment().add('minutes',30);
},
setReservationType: function(reservationType){
this.reservationType = reservationType;
this.reservationTypeValidate = true;
this.emit('change-reservation-type', this.reservationType);
this.emit('change');
}
setPickupTime: function(pickupTime){
this.pickupTime = pickupTime;
this.pickupTimeValidate = true;
this.emit('change-pickup-time', this.pickupTime);
this.emit('change');
}
});
What's the best way to set state based on the data received from observe()?
It seems setting state via componentWillMount() won't work as observe() runs after this and the data isn't available to set state.
I'm using the observe function as advised when using Parse
E.g.:
var DragApp = React.createClass({
getInitialState: function () {
return {
activeCollection : ''
};
},
observe: function() {
return {
collections: (collectionsQuery.equalTo("createdBy", currentUser))
};
},
_setactiveCollection: function(collection) {
this.setState({
activeCollection : collection
});
},
componentWillMount: function () {
var collection = this.data.collections[0];
this._setActiveCollection(collection);
},
)}
I went the wrong way about this.
I shouldn't be storing this.data into state. I can pass it into components via render.
To get round this.data not being ready before rendering, I make use of the ParseReact function pendingQueries() inside render. E.g.
if (this.pendingQueries().length > 0) {
content = 'loading...'
} else {
content = 'hello world I am' + this.data.name
}
Try:
var DragApp = React.createClass({
observe: function() {
var collections = collectionsQuery.equalTo("createdBy", currentUser);
return {
collections: collections,
activeCollection: collections[0]
};
},
render: function () {
// do something with this.data.collections and/or this.data.activeCollection
},
)}
I'm having an issue with creating a component using react and martyjs. I'm sure it is a typo or something but I just can't seem to find it. Although I have a state mixin in the component the state is not being populated and it doesn't look like getState is even being called in the mixin.
Mixin.es6
var StateMixin = Marty.createStateMixin({
listenTo: VideoStore,
getState: function() {
return {
items: VideoStore.list(),
currentItem: VideoStore.select(),
}
}
});
State.es6
var VideoStore = Marty.createStore({
displayName: "Store",
handlers: {
list: Events.List,
render: Events.Render
},
getInitialState: function(){
return { };
},
list: function(){
return this.fetch({
id: 'list',
locally: function(){
if(this.hasAlreadyFetched('list') )
return this.state.items;
},
remotely: function(){
return DissolveStateSource.list();
}
});
},
select: function(){},
render: function(){}
});
Component.es6
$( ()=>
React.render(
<VideosTable/>,
$("#container")[0]
));
var VideosTable = React.createClass(
{
mixins: StateMixin,
render: function() {
var body = this.state.list.when({ //state is null here
pending: function(){
return <span className="ball"></span>;
},
failed: function(error){
return <div className="error">error.message</div>;
},
done: function(videos){
return <div>Videos</div>;
}
});
return <h2>hello</h2>;
}
});
Any idea what I'm doing wrong?
Edit: I've added a js bin thing here
http://jsbin.com/lekegicumo/2/edit?html,js,console,output
Looks like a typo in Mixin.es6 to me.
Change getState to getInitialState.
Also, in Component.es6:
Change mixins: StateMixin to mixins: [StateMixin].
The problem ended up being that the order of inclusion of JavaScript files was incorrect. Swapping some around fixed the issue.
are u using react v0.1.13.0
this is new way to initial your state using 'construct'
constructor(props) {
super(props);
this.state = {count: props.initialCount};
}
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html