Uncaught Invariant Violation on rendering search data - javascript

I am trying to implement search using React. I have 2 problems on my logic flow:
To set input as Params
To render the data I get from server
While I'm playing with it, I have encountered the error message
Uncaught Invariant Violation input is a void element tag and must not
have children or use props.dangerouslySetInnerHTML.
And this is my code:
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './Home.scss';
import AWS from 'aws-sdk';
var GetTech = React.createClass({
render: function() {
var createItem = function(item) {
var csd = new AWS.CloudSearchDomain({
endpoint: 'mycloudsearch.amazonaws.com',
region: 'us-east-1'
});
var params = {
query: {this.state.text}
}
csd.search(params, function (err, data) {
if (err) console.log(err, err.stack);
else {
console.log(JSON.stringify(data));
}
});
}
return (
{this.props.items.map(crateItem)}
)
}
});
var FilteredTechs = React.createClass({
getInitialState: function() {
return {
text: '',
items: []
};
},
handleChange: function(event) {
console.log(event);
this.setState({
text: event.target.value
});
},
handleSearch: function(event) {
event.preventDefault();
this.setState({
items: this.props.items,
text: ''
});
},
render: function() {
return (
<div>
<form onSubmit={this.handleSearch}>
<input
type="text"
value={this.state.text}
onChange={this.handleChange}
/>
<input type="button">Search</input>
</form>
<GetTech items={this.state.items} />
</div>
);
}
});
function Home({ techs }) {
<FilteredTechs />
}
Home.propTypes = {
techs: PropTypes.arrayOf(PropTypes.shape({
})).isRequired,
};
export default withStyles(Home, s);
I am new to React. Please advise me as you wish and your tips and comments are very appreciated. Thanks a lot!

The error is pretty clear: inputs must be void elements; that is, they must be self-closing.
This syntax is invalid: <input type="button">Search</input>
You want either: <input type="button" value="Search" />
Or: <button>Search</button>

Input is self-closing tag, However there some cases in which you can use <Input></Input>
You can install reactstrap package then import and use <Input></Input> tag instead of using <input></input>.
Also, you can check the link: https://reactstrap.github.io/components/input-group/
Also, you can check this for the type of input: reactstrap Forms

Related

Getting values from FormPanel in ExtReact 6.6.0

How should I be getting values from a FormPanel using ext-react 6.6.0?
According to the API documentation I should be using getValues function, that works in 6.5.1 but I get error _this.form.getValues is not a function in 6.6.0
Code
Works in 6.5.1: https://fiddle.sencha.com/?extreact#view/editor&fiddle/2n05
Fails in 6.6.0 (see console for error): https://fiddle.sencha.com/?extreact#view/editor&fiddle/2n04
I get error _this.form.getValues is not a function in 6.6.0
The reason ref={form => this.form = form}. In extreact-6.6.0 the form variable is not exact formpanel. So for this you need to access like this
ref={form => this.form = (this.form || form.cmp)}}
Another way you use button.up('formpanel') to get the formpanel component. This button is first parameter of your handler.
button.up('formpanel').getValues()
You can check here with working fiddle.
Code Snippet
import React, { Component } from 'react';
import {launch} from '#sencha/ext-react';
import { ExtReact } from '#sencha/ext-react';
import { Container, Label, FormPanel, TextField, Button } from '#sencha/ext-modern';
class App extends Component {
state = {
values:JSON.stringify({
fname: 'null',
lname: 'null'
})
}
submit = (btn) => {
const values = btn.up('formpanel').getValues();
console.log('Values using up selector',values);
console.log('Values using up ref',this.form.getValues());
this.setState({values:JSON.stringify(this.form.getValues())});
}
render() {
return (
<Container defaults={{ margin: 10, shadow: true }}>
<FormPanel title="Form" ref={form => this.form = (this.form || form.cmp)}>
<TextField name="fname" label="First Name"/>
<TextField name="lname" label="Last Name"/>
<Button handler={this.submit} text="Submit"/>
</FormPanel>
<Label padding={'10'} html={this.state.values} />
</Container>
)
}
}
launch(<ExtReact><App /></ExtReact>);

Listening to API events in Vue

So I am struggling to find a way to listen to API events in vue. Tried to use different lifecycle hooks like mounted.
I am using this chat API: https://www.pubnub.com/docs/chat-engine/chats
And trying to listen when someone else has send out a message.
this.chat.on('message', (payload) => {
console.log(payload)
})
But I dont know where to put it. I used mounted but it didnt work. They also have an example for React here: https://github.com/pubnub/chat-engine-examples/blob/master/react/src/index.js and they are using the componentDidMount.
Does anyone where I can listen to the API events?
This works for me. I've auto created the application on pubnub https://www.pubnub.com/docs/chat-engine/getting-started#automagic-pubnub-setup
<template>
<div id="chat">
<div v-if="!isLoading">
<div>
<form #submit.prevent="sendMessage">
<input v-model="text"
type="text"
placeholder="Say something I am giving up on you" />
</form>
<div v-for="(message, index) in messages" :key="index">
{{message.text}}
</div>
</div>
</div>
<div v-else>loading...</div>
</div>
</template>
<script>
import ChatEngineCore from 'chat-engine';
const ChatEngine = ChatEngineCore.create({
publishKey: 'pub-c-e1fbdcd1-b3a9-4a67-b184',
subscribeKey: 'sub-c-b023a266-1628-11e8-92ea'
});
const now = new Date().getTime();
const username = ['user', now].join('-');
export default {
name: 'Chat',
data() {
return {
chat: null,
me: null,
isLoading: true,
text: '',
messages: []
};
},
created() {
this.init();
},
methods: {
init() {
ChatEngine.connect(
username,
{
signedOnTime: now
},
'auth-key'
);
ChatEngine.on('$.ready', data => {
this.isLoading = false;
this.me = data.me;
this.chat = new ChatEngine.Chat('ChatTest');
this.chat.on('message', message => {
this.messages.push(message.data);
});
});
},
sendMessage() {
this.chat.emit('message', {
text: this.text
});
}
}
};
</script>

React - Unable to click or type in input form using react-tagsinput and react-mailcheck

I am using react-tagsinput, react-input-autosize, and react-mailcheck to create input tags that also suggests the right domain when the user misspell it in an email address.
I have gotten the react-tagsinput to work with react-input-autosize but when added with react-mailcheck my input form does not work at all, the form is un-clickable and unable to type and text into the field. I'm not getting any errors in the console and i'm not sure what is wrong with my code. I followed what they did in the react-mailcheck documentation: https://github.com/eligolding/react-mailcheck. I was hoping someone could look at it with a fresh pair of eyes to see what I am missing that is making it not work.
Thanks!
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TagsInput from 'react-tagsinput';
import AutosizeInput from 'react-input-autosize';
import MailCheck from 'react-mailcheck';
class EmailInputTags extends Component {
static propTypes = {
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
};
constructor() {
super();
this.state = { tags: [], inputText: '' };
this.handleChange = this.handleChange.bind(this);
this.handleInputText = this.handleInputText.bind(this);
this.renderInput = this.renderInput.bind(this);
}
handleChange(tags) {
this.setState({ tags });
}
handleInputText(e) {
this.setState({ inputText: e.target.value });
}
renderInput({ addTag, ...props }) {
const { ...other } = props;
return (
<MailCheck email={this.state.inputText}>
{suggestion => (
<div>
<AutosizeInput
type="text"
value={this.state.inputText}
onChange={this.handleInputText}
{...other}
/>
{suggestion &&
<div>
Did you mean {suggestion.full}?
</div>
}
</div>
)}
</MailCheck>
);
}
render() {
const { label, name } = this.props;
return (
<div className="input-tag-field">
<TagsInput inputProps={{ placeholder: '', className: 'input-tag' }} renderInput={this.renderInput} value={this.state.tags} onChange={this.handleChange} />
<label htmlFor={name}>{label}</label>
</div>
);
}
}
export default EmailInputTags;
I have not tried this out.
Try passing as a prop to TagsInput the onChange function.
ie
{... onChange={(e) => {this.setState(inputText: e.target.value}}

React JS: Reusable components

I have created the form validation with a structure like this
var Signin = React.createClass({
render: function() {
return (
<Form>
<Input type="text" name="email" labelName="Email" rules="isEmail" error:"Email not valid" />
<Input type="password" name="password" labelName="Password" rules="isLength:6" error:"Passowrd not valid"/>
</Form>
);
}
});
because, for example, the "Email" input will be used in different part of application, I would avoid to add the same attributes (name, type, labelName, rules and error) every time. So I would create something like this
var InputEmail = React.createClass({
render: function () {
return (
<Input type="text" name="email" labelName="Email" rules="isEmail" error="Email not valid"/>
)
}
});
var InputPassword = React.createClass({
render: function () {
return (
<Input type="password" name="password" labelName="Password" rules="isLength:6" error="Passwordnot valid"/>
)
}
});
So the Signin Component should be
var Signin = React.createClass({
render: function() {
return (
<Form>
<InputEmail />
<InputPassword />
</Form>
);
}
});
but in this way, I get two errors:
I can't find anymore in the Form the props.name of Input because
there isn't in InputEmail;
in the render function of Input the state is null
How could I create a reausable/inherits components? I failed using both the composition pattern and the mixins
I added my full code: Form
var Form = React.createClass({
getInitialState: function () {
return {
isValid : false,
isSubmitting: false
}
},
componentWillMount: function(){
this.model = {};
this.inputs = {};
this.registerInputs(this.props.children);
},
registerInputs: function(children){
React.Children.forEach(children, function (child) {
if (child.props.name) {
child.props.attachToForm = this.attachToForm;
child.props.detachFromForm = this.detachFromForm;
child.props.validate = this.validate;
}
if (child.props.children) {
this.registerInputs(child.props.children);
}
}.bind(this));
},
attachToForm: function (component) {
this.inputs[component.props.name] = component;
this.model[component.props.name] = component.state.value;
this.validate(component);
},
detachFromForm: function (component) {
delete this.inputs[component.props.name];
delete this.model[component.props.name];
},
validate: function (component) {
var isValid = true;
// validation code
component.setState({
isValid: isValid,
}, this.validateForm);
},
validateForm: function () {
var formIsValid = true;
var inputs = this.inputs;
Object.keys(inputs).forEach(function (name) {
if (!inputs[name].state.isValid) {
formIsValid = false;
}
});
this.setState({
isValid: formIsValid
});
},
updateModel: function (component) {
Object.keys(this.inputs).forEach(function (name) {
this.model[name] = this.inputs[name].state.value;
}.bind(this));
},
submit: function (event) {
event.preventDefault();
this.setState({
isSubmitting : true
});
this.updateModel();
console.log(this.model);
},
render: function () {
return (
<form className="ui form" onSubmit={this.submit}>
{this.props.children}
<button className="ui button" type="submit" disabled={this.state.isSubmitting}>Accedi</button>
</form>
);
}
});
Input
var Input = React.createClass({
getInitialState: function(){
return {
value : this.props.value || "",
isValid: true
}
},
setValue: function (event) {
this.setState({
value: event.target.value
}, function () {
this.props.validate(this);
}.bind(this));
},
componentWillMount: function () {
if (this.props.required) {
this.props.validations = this.props.validations ? this.props.validations + ',' : '';
this.props.validations += 'isLength:1';
}
// ERROR: TypeError: this.props.attachToForm is not a function
this.props.attachToForm(this);
},
componentWillUnmount: function () {
this.props.detachFromForm(this);
},
render: function () {
var className = "field";
if(this.props.className){
className += " " + this.props.className;
}
if(this.props.required){
className += " required";
}
var Label;
if(this.props.labelName){
Label = (<label htmlFor={this.props.name}>{this.props.labelName}</label>);
}
var Error;
if(!this.state.isValid){
Error = (<div className="ui">{this.props.error || this.props.name + " not valid"}</div>);
};
return (
<div className={className}>
{Label}
<input type={this.props.type || "text"} id={this.props.name} name={this.props.name} onChange={this.setValue} value={this.state.value} />
{Error}
</div>
);
}
});
With this works
ReactDOM.render(
<Form>
<Input type="text" name="email" labelName="Email" rules="isEmail" error:"Email not valid"/>
</Form>,
document.getElementById('app')
);
In this way I get:
"TypeError: this.props.attachToForm is not a function
this.props.attachToForm(this);"
ReactDOM.render(
<Form>
<InputEmail/>
</Form>,
document.getElementById('app')
);
P.S: I tried to add this code on jsfiddle but I get "!TypeError: can't define property "attachToForm": Object is not extensible"
jsfiddle
There are 2 main issues with your setup:
Your <Form> is set up in such a way, that the children of the form need to have props, otherwise it does not work.
The <InputEmail> wrapper is incomplete. It needs to pass along all props to the <Input>, including the Form functions passed down.
Ad 1: Fix the form, to ensure validation methods are added
The reason you get the error is because the children of your <Form> need to have props.name. It then registers the functions of the form (including attachToForm), by adding them to the children. This is done in the method registerInputs().
In the original variant, the <Input> component has props, so all goes well.
In the adapted variant, the wrapper <InputEmail> no longer has props, so the attachToForm() and other functions are not added to props, and you get the error when the <Input> tries to invoke the function.
Simplest way to fix this: add at least 1 prop in the render function, and check this in the registerInputs(), e.g.:
ReactDOM.render(
<Form>
<InputEmail initialValue={'name#domain.com'}/>
</Form>,
document.getElementById('app')
);
And in registerInputs(), change the line:
if (child.props.name) {
to:
if (child.props.initialValue) {
2. Extend <InputEmail> wrapper to pass down functions as well
Simplest way to do this is to add {...this.props}, like this:
var InputEmail = React.createClass({
render: function () {
return (
<Input {...this.props}
type="text" name="email" labelName="Email" rules="isEmail" error="Email not valid"/>
)
}
});
That way, the functions passed down by <Form> to the <InputEmail> component (as well as any other props), will be passed down to the <Input> component.
PS: The code inside registerInputs() that checks for child.props.children does not work as intended: at the time it is invoked, the <InputEmail> component does not have children. Like the name implies, it checks for children passed down as props. And the only prop passed was initialValue.
As minor issues I would suggest to make 2 more changes:
In registerInputs(), you directly modify props. This is generally not a good idea. Better is to make a copy of props, and add your form-methods to the copy. You can use React.Children.map to do this. See official docs here.
Instead of hard-coding the name="email" etc of your <Input> component, inside <InputEmail>, better is to put the default values of these in default values of props, using propTypes, as explained here in official docs.

React get value deep inside DOM

I am not so familiar with react, I try to build the following abstraction over sign in / forms etc.
Take a look at this:
var SignUpForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
console.log(this.refs.iitu_id.getDOMNode().value.trim())
// iitu_id = this.refs.iitu_id.getDOMNode().value.trim();
// password = this.refs.password.getDOMNode().value.trim();
var error = UserValidator.valid({iitu_id: iitu_id, password: password});
if (error) {
this.setState({"errors": error });
// console.log(error);
} else {
// console.log(error);
}
},
getInitialState: function() {
return {
'errors': {
iitu_id: null,
password: null
}
};
},
render: function() {
return (
/*jshint ignore:start */
<form className="form-horizontal" onSubmit={this.handleSubmit} >
<FormGroup label="iitu id" error_msg={this.state.errors.iitu_id} fieldName="iitu_id" fieldType="text" />
<FormGroup label="password" error_msg={this.state.errors.password} fieldName="password" fieldType="password" />
<ButtonGroup text="Войти"/>
</form>
/*jshint ignore:end */
);
}
});
var FormGroup = React.createClass({
render: function() {
var formGroupCss = 'form-group';
if (this.props.error_msg){
formGroupCss = 'form-group has-error';
}
return (
/*jshint ignore:start */
<div className={formGroupCss}>
<Label fieldName={this.props.fieldName}>{this.props.label}</Label>
<InputField type={this.props.fieldType}>{this.props.label}</InputField>
<label className="control-label" for="inputError1">{this.props.error_msg}</label>
</div>
/*jshint ignore:end */
);
}
});
var ButtonGroup = React.createClass({
render: function () {
return (
/*jshint ignore:start */
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">
<button className="btn btn-default">{this.props.text}</button>
</div>
</div>
/*jshint ignore:end */
);
}
});
var InputField = React.createClass({
render: function() {
return (
/*jshint ignore:start */
<div className="col-sm-5">
<input className="form-control" type={this.props.fieldType} placeholder={this.props.children}/>
</div>
/*jshint ignore:end */
);
}
});
exports.SignUpForm = SignUpForm;
there is a bit too much code, but I think it is pretty easy to read. The question is how can I get the value of my InputField class when I press the submit button (simply get form value)? There is problem that my input tag deep inside DOM. Additional question is it good to have the following code design I mean describe each logical component as new 'class' ?
If you give your form inputs name attributes, you can use the form's .elements collection to access its inputs.
I recently split the code newforms uses for this out into a reusable get-form-data module, which would let you do this assuming your inputs have name attributes:
var getFormData = require('get-form-data'); // Or just use the browser bundle
var SignUpForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var data = getFormData(e.target, {trim: true});
var error = UserValidator.valid(data);
if (error) {
this.setState({errors: error});
} else {
// ...
}
},
// ...
Or, if you need to get input as it's given, you can add an onChange handler to the <form> or some other component which contains all the form inputs, instead of having to do each one individually:
handleChange: function(e) {
var form = e.target.form;
var name = e.target.name;
var data = getFormData.getNamedFormElementData(form, name, {trim: true});
// ...
},
render: function() {
return <form onSubmit={this.handleSubmit} onChange={this.handleChange}>
{/* ... */}
</form>
}
});
In the absence of a framework like Angular or Flux, shoot data between your components using callbacks.
The technique used here is explained in detail on React's website. Another solution, however, is to use a form library. I took a similar route and tried to build form components from scratch -- it works, but it's a path others have already cleared. Check out newforms and react-forms.
Note that the code I shared is untested, though it should be very close to working.
var SignUpForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
console.log(this.iitu_id + ' and ' + this.password + ' should work.');
var error = UserValidator.valid({iitu_id: iitu_id, password: password});
if (error) {
this.setState({"errors": error });
// console.log(error);
} else {
// console.log(error);
}
},
getInitialState: function() {
return {
'errors': {
iitu_id: null,
password: null
}
};
},
updateForm: function(field, value) {
/* Or do something like this using underscore.js:
var form = _.clone(this.form);
form[field] = value;
this.setState({ form: form });
This approach let's you grab the form object on
submit instead of hunting for the form values
in SignUpForm's state.
*/
this.setState({ field: value });
},
render: function() {
return (
/*jshint ignore:start */
<form className="form-horizontal" onSubmit={this.handleSubmit} >
<FormGroup update={this.updateForm} label="iitu id" error_msg={this.state.errors.iitu_id} fieldName="iitu_id" fieldType="text" />
<FormGroup label="password" error_msg={this.state.errors.password} fieldName="password" fieldType="password" />
<ButtonGroup text="Войти"/>
</form>
/*jshint ignore:end */
);
}
});
var FormGroup = React.createClass({
handleChange: function(event) {
this.props.update(this.props.fieldName, event.target.value);
},
render: function() {
var formGroupCss = 'form-group';
if (this.props.error_msg){
formGroupCss = 'form-group has-error';
}
return (
/*jshint ignore:start */
<div className={formGroupCss}>
<Label fieldName={this.props.fieldName}>{this.props.label}</Label>
<InputField handleChange={this.handleChange} type={this.props.fieldType}>{this.props.label}</InputField>
<label className="control-label" for="inputError1">{this.props.error_msg}</label>
</div>
/*jshint ignore:end */
);
}
});
var ButtonGroup = React.createClass({
render: function () {
return (
/*jshint ignore:start */
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">
<button className="btn btn-default">{this.props.text}</button>
</div>
</div>
/*jshint ignore:end */
);
}
});
var InputField = React.createClass({
render: function() {
return (
/*jshint ignore:start */
<div className="col-sm-5">
<input onChange={this.props.handleChange} className="form-control" type={this.props.fieldType} placeholder={this.props.children}/>
</div>
/*jshint ignore:end */
);
}
});
exports.SignUpForm = SignUpForm;
Specifically, take note of the callback functions being passed from parent to child.
SignUpForm gives updateForm to FormGroup
FormGroup gives handleChange to InputField
Then to get the data back to the top, you just reverse the process.
When <input/> changes, it runs handleChange
When handleChange runs, it passes the FormGroup's fieldName and value to SignUpForm
SignUpForm updates its state with the new field value
Concerning your second question - it is a bad approach to add a component for every logical element.
In your example - all of the components, (besides the first) has no logic, only 'render'. this design adds many useless lines of code and "this.props" repetitions.
In your example, I would have just add everything to the render of the first component.
Now, let say you have two components, and want to reach the child from the parent:
var SignUpForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
//now - reach your inner comp with refs
console.log(this.refs.inner.refs.username.getDOMNode().value.trim());
},
render: function() {
return (
//and pass the submit function as a callback
<InnerForm ref="inner" submit={this.props.handleSubmit}/>
);
}
});
var InnerForm = React.createClass({
render: function() {
return (
<form>
<input type="text" ref="username"/>
<input type="submit" onClick={this.props.submit} >
</form>
);
}
});
In your example there was to many components inside components to do what I did.
Still, there are cases when you need long linage of child components. Using this.refs.a.refs.b.refs.c etc - might be pretty ugly, and also prevents re-usability.
in those cases try using any MVC architecture (I'm using reflux and loving it)
I would suggest an onChange event listener to your inputs that use callbacks to relay the information all the way back up to your top level component.
var SignUpForm = React.createClass({
getInitialState: function() {
return {
inputs: {
id: '',
password: ''
}
};
},
render: function() {
return (
<form className="form-horizontal" onSubmit={this.handleSubmit} >
<FormGroup ... callback={this.handleIdChange} />
<FormGroup ... callback={this.handlePasswordChange} />
<ButtonGroup text="Войти"/>
</form>
);
},
handleIdChange: function(newId) {
this.setState({
inputs: {
id: newId.target.value,
password: this.state.inputs.password
}
});
},
handlePasswordChange: function(newPassword) { ... }
});
then pass that callback down to the base level form fields and use them for handling the onChange event.
var InputField = React.createClass({
render: function() {
return (
/*jshint ignore:start */
<div className="col-sm-5">
<input ... onChange={this.props.callback}/>
</div>
/*jshint ignore:end */
);
}
});
then with your handleSubmit just output the values in the this.state.inputs object.

Categories

Resources