I'm using React to make a Markdown previewer and trying to make sense of what was used here to make the right box update with a live preview as soon as the text is changed on the left. They have this code at the bottom:
var RawInput = React.createClass({
update:function(){
var modifiedValue=this.refs.inputValue.getDOMNode().value;
this.props.updateValue(modifiedValue);
},
render:function(){
return (<textarea rows="22" type="text" ref="inputValue" value={this.props.value} onChange={this.update} className="form-control" />)
}
});
I implementing this in my code:
const InputText = React.createClass ({
update() {
let newValue = this.refs.inputValue.getDOMNode().value;
this.props.updateValue(newValue);
},
render() {
return (
<div>
<textarea
id="input-text"
rows="18"
type="text"
ref="inputValue"
value={this.props.value}
onChange={this.update}
/>
</div>
);
}
});
The app runs fine except that there is no live preview and the text on the right doesn't update. In the console I get this error: this.refs.inputValue.getDOMNode is not a function .
Here is the full code:
import React from 'react';
import Banner from './components/Banner.jsx';
import ContainerHeader from './components/ContainerHeader.jsx';
import marked from 'marked';
const App = React.createClass ({
updateValue(newValue) {
this.setState ({
value: newValue
})
},
getInitialState() {
return {
value: 'Heading\n=======\n\nSub-heading\n-----------\n \n### Another deeper heading\n \nParagraphs are separated\nby a blank line.\n\nLeave 2 spaces at the end of a line to do a \nline break\n\nText attributes *italic*, **bold**, \n`monospace`, ~~strikethrough~~ .\n\nShopping list:\n\n * apples\n * oranges\n * pears\n\nNumbered list:\n\n 1. apples\n 2. oranges\n 3. pears\n'
}
},
markup(value) {
return {__html: value}
},
render() {
return (
<div>
<Banner />
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-6">
<ContainerHeader text="I N P U T" />
<InputText
value={this.state.value}
updateValue={this.updateValue} />
</div>
<div className="col-xs-12 col-sm-6">
<ContainerHeader text="O U T P U T" />
<div id="output-text">
<span dangerouslySetInnerHTML={this.markup(marked(this.state.value))}></span>
</div>
</div>
</div>
</div>
</div>
);
}
});
const InputText = React.createClass ({
update() {
let newValue = this.refs.inputValue.getDOMNode().value;
this.props.updateValue(newValue);
},
render() {
return (
<div>
<textarea
id="input-text"
rows="18"
type="text"
ref="inputValue"
value={this.props.value}
onChange={this.update}
/>
</div>
);
}
});
export default App;
Any help welcome on solving this, and thanks!
Since version 15.* in React this.refs.inputValue refers to DOMElement, so you don't need use getDOMNode;
this.refs.inputValue.value
However, I think in this case you don't need use refs, as you call update inside onChange event, you can get target(refer to textarea) from event object, and from target get value
update(e) {
this.props.updateValue(e.target.value);
},
The example is using React v0.14.x which was one combined module.
From React v15 (0.15, but they changed to use it as the major) the methods and classes were split into two modules, React and ReactDOM.
You need to import and use ReactDOM.findDOMNode(component), documentation for which can be found here.
You actually do not need this.refs.inputValue
update(e) {
this.props.updateValue(e.target.value);
},
Related
I'm trying to create a form with several fields, but I can't get my head around this one. It seems simple, but most approaches I've taken are messy or didn't work. I'm trying to get the following result:
<div>
<input />
</div>
<div>
<input />
<input />
</div>
No matter what I try, these divs always end up on their own line. What am I doing wrong here?
Codesandbox
Here is what you want:
import React from "react";
import "./styles.css";
const renderFields = fields =>
fields.map(({ name }) => (
<div style={{ display: name !== "fieldA" ? "inline-block" : "" }}>
<input />
</div>
));
export default function App() {
const fields = [{ name: "fieldA" }, { name: "fieldB" }, { name: "fieldC" }];
return renderFields(fields);
}
A working example of my problem can be found at:
https://codepen.io/RyanCRickert/pen/vYYQeaW
I am prop drilling a function two levels and passing that function along with an index to a rendered component. When a name is submitted it renders a new component which shows the name and div which has an onClick (X). I am trying to receive the index of where the name is located in the array which it lives so that I may splice it out when the button is clicked.
If I enter the name "Bob" for example, then click the div with the listener I can console log the event.target. Using the above example I get "<div class='person-item__X' value='0'>X</div>" for event.target and undefined for event.target.value. The value is being assigned as <div onClick={props.removeName} class="person-item__X" value={props.value}>X</div>.
Am I just unable to grab the value of a div in such a manor? Or is there something that I am missing? Thank you
Change these to your code
const PersonListItem = props => (
<div class="person-item">
<div class="person-item__name">{props.name}</div>
<div onClick={() => props.removeName(props.value)} class="person-item__X" value={props.value}>X</div>
</div>
);
Inside PeopleList replace this line
<PersonListItem key={index} name={person} value={index} removeName={(id) => props.removeName(id)} />
Inside TeamGenerator replace this line
<PeopleList people={this.state.names} removeName={(id) => this.handleRemoveName(id)} />
now in handleRemoveName you will recieve a id of the item on which X was clicked
handleRemoveName = id => {
const currentArr = this.state.names;
console.log(id);
}
In your case, to grab the value inside this div, you should use ref API.
Your code should look like this:
TeamGenerator.js
import React, { Component } from "react";
import CustomModal from "./Modal";
import PeopleList from "./PeopleList";
import "./index.css";
export default class App extends Component {
constructor(props) {
super(props);
// Create a ref
this.divTextRef = React.createRef();
this.state = {
names: [],
selectedName: ""
};
}
handleCloseModal = () => {
this.setState({
selectedName: ""
});
};
handleChange = e => {
this.setState({ name: e.target.value });
};
handleRemoveName = index => {
// Get your name and index this way
console.log("Your text: ", this.divTextRef.current.innerHTML);
console.log("Your index: ", index);
};
handleSubmit = e => {
e.preventDefault();
const currentNames = this.state.names;
if (this.state.name)
currentNames.push(
this.state.name[0].toUpperCase() + this.state.name.slice(1)
);
this.setState({
name: "",
names: currentNames
});
};
render() {
return (
<div className="container">
<CustomModal
selectedName={this.state.selectedName}
closeModal={this.handleCloseModal}
/>
<form onSubmit={this.handleSubmit}>
<label>
Add name:
<input
type="text"
value={this.state.name}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
<div className="people-list-container">
<PeopleList
people={this.state.names}
removeName={this.handleRemoveName}
upperRef={this.divTextRef} // Pass the ref down from your Component tree
/>
</div>
</div>
);
}
}
PeopleList.js
import React from "react";
import PersonListItem from "./PersonListItem";
export default class PeopleList extends React.Component {
render() {
return (
<div className="people-container">
<div className="people-title">List of people</div>
<div className="people-list">
{this.props.people.length === 0 ? (
<div className="people-item">
<span>No people added</span>
</div>
) : (
this.props.people.map((person, index) => (
<PersonListItem
key={index}
name={person}
value={index}
removeName={() => this.props.removeName(index)} // Passing index to the removeName function of Parent
upperRef={this.props.upperRef} // Continue passing it down to PersonListItem
/>
))
)}
</div>
</div>
);
}
}
PersonListItem.js
import React from "react";
const PersonListItem = props => (
<div className="person-item">
<div ref={props.upperRef} className="person-item__name"> // Use the passed ref
{props.name}
</div>
<div
onClick={props.removeName}
className="person-item__X"
value={props.value}
>
X
</div>
</div>
);
export default PersonListItem;
The div node does not have the value like input, so you can not grab it by your old way.
I have a dynamically created from in React, and I'd like to be able to submit the values of all the input fields, but I can't add seperate on change handlers for each input elment, as they are created dynamically:
extract from the form js:
const FormElements = ({formFields}) => ( <div> {
formFields.map(formField => ( <FormElement name={formField.name} type={formField.fieldType} />)
)} </div> );
console.log(formFields);
return (
<div class="col-md-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h4 class="panel-title">
{title} - {id}
</h4>
</div>
<div class="panel-body">
<form >
<FormElements formFields={formFields} />
<a
class="btn btn-primary"
onClick={this.handleSubmitButton}//what do I do with this function?
href="#">Submit</a>
</form>
</div>
</div>
</div>
);
form element js:
export default class FormElement extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div class="form-group">
<label for="{this.props.name}">{this.props.name}</label>
<input type="{this.props.type}}" class="form-control" id="{this.props.name}" placeholder="blah blah" />
</div>
);
}
}
Since they are controlled inputs there is not a react way to that, and even if there is I would not recommend it, React is all about declarative code.
There are two ways to solve this, one is to use make a property onChange on your FormElement and pass a function with ids, something like this:
<FormElements onChange={(key, value) => this.setState({ [key]: value })
The other way is to send give all the not defined props to the input:
export default class FormElement extends React.Component {
constructor(props) {
super(props);
}
render() {
const { name, type, ...other } = this.props
return (
<div class="form-group">
<label for="{name}">{name}</label>
<input type="{type}}" class="form-control" {...other} id="{this.props.name}" placeholder="blah blah" />
</div>
);
}
}
(the { [key]: value } and {...other} is ES6)
I actually managed this in a quite convoluted, and probably not recommended way, but it works! I've also never seen this done elsewhere...probably for good reason:
Form element:
export default class FormElement extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
onChange(e) {
this.props.handleChange(e.target.id, e.target.value);
}
render() {
return (
<div class="form-group">
<label for={this.props.id}>{this.props.name}</label>
<input type="{this.props.type}}" class="form-control" id={this.props.id} value={this.props.value} placeholder="blah blah" onChange={this.onChange}/>
</div>
);
}
}
form:
handleFormElementChange(id, value) {
console.log("changing: " + id + " = "+ value);
var frm = this.state.formData;
var index=-1;
for(var i=0;i<frm.length;i++) {
if(frm[i].id==id) {
index=i;
break;
}
}
frm[index].value = value;
this.setState({formData: frm});
}
const FormElements = ({formFields}) => ( <div> {
formFields.map(formField => ( <FormElement name={formField.name} key={formField.id} value={formField.value} id={formField.id} type={formField.fieldType} handleChange={this.handleFormElementChange.bind(this)}/>)
)} </div> );
What's happening is the actual full form data is being updated in the form component, and each time a change is made to one of the form elements, it passes it back to the parent form, update's the form's sate and then re-renders the whole form.
The complication here was actually finding the correct form element in the overall form status, by searching through the array for the key, and updating the value.
While I see this working with small forms, I can see how it would start to significantly slow down rendering on large form applications.
I'm having hard time writing test unit with Jest :/ Here is my code:
import { NewRec } from '../src/components/edit';
import { shallow } from 'enzyme';
import React from 'react/lib/ReactWithAddons';
jest.mock('react-dom');
jest.mock('react/lib/ReactDefaultInjection');
describe('NewRec component', () => {
const component = shallow(<NewRec />);
it('returns true if blah blah', ()=>{
const p = component.find('errortitle');
expect(p.length).toEqual(1)
});
});
P is always 0. I tried to check how the component looks like after rendering. Asked the question here: (How to see what the rendered React component looks like in the Jest unit test?)
The snapshot file says it's null:
exports[`NewRec component returns true if blah blah 1`] = `null`;
So why it's always null? What is the problem in the code? The "NewRec" component is using mixins (mixins: [React.addons.LinkedStateMixin]). Can that cause the problem?
UPDATE: Here is the component code:
export const NewRec = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState() {
return {
group_id: null,
title: "",
errors: {},
}
},
createRec(event) {
event.preventDefault();
if (!this.state.title.length) {
this.setError('title', "Please add a title");
return;
}
if (!this.state.group_id) {
this.setError('group', "Please select a group");
return;
}
serverCache.createRec(
{ group: this.state.group_id, title: this.state.title },
rec => { browser.gotoRec(rec.id); }
);
},
selectgroup(group_id) {
this.setState({group_id: group_id});
},
rendergroupList(groups) {
return (
<div > { groups.map(this.rendergroup) } </div>
);
},
onTitleChange(event) {
this.setState({title:event.target.value});
},
componentWillMount() {
const user = serverCache.getUser();
if (!user || !user.get('name')) {
notifications.warning('Please login first.');
}
},
render() {
const user = serverCache.getUser();
const groups = serverCache.getgroups();
return (
<div className="row">
<form className="form-horizontal" onSubmit={this.createRec}>
<label htmlFor="title" className="col-sm-3 control-label"> Title </label>
<div>
<input type="text" id='title' value={this.state.title} onChange={this.onTitleChange} />
</div>
<div>
<label htmlFor="group" className="col-sm-3 control-label" >group </label>
</div>
<div>
{this.state.errors.title ? <div id='errortitle' >{this.state.errors.title} </div>: false }
{this.state.errors.group ? <div id='errorgroup' >{this.state.errors.group} </div> : false }
<div >
<button type="submit" className="btn btn-primary btn-default btn-block"> Create Rec</button>
</div>
</div>
</form>
</div>
);
}
});
I think component.find('new-rec').length is 0 because there is no new-rec component, tag, or whatever you're looking for, I mean, wehen I see this code const p = component.find('new-rec'); I understand that you are trying to get a p tag with a new-rec class or something like that, But if you are looking for a class, then you have to do const p = component.find('.new-rec'); Note de dot before de word (css selector). But again it will be 0 because I don't see any p tag with a .new-rec class in the NewRec component.
After having worked with React.js a couple of days, I have written most of my forms like the following (as exemplified in the official tutorial):
React.createClass({
handleSubmit: function (event) {
event.preventDefault();
var value = React.findDOMNode(this.refs.text).value;
// do something with the value
},
render: function () {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" ref="text" defaultValue="foo" />
<input type="submit" value="Save"/>
</form>
);
}
}
However, I have recenctly discovered a weakness of this approach where I am not capable to write rich form components to be used in forms that are built up by such components such as:
var RichInput = React.createClass({
render: function() {
return (
<div className="someStyle">
<input type="text" ref="text" defaultValue="foo" />
</div>
);
}
}
React.createClass({
handleSubmit: function (event) {
event.preventDefault();
var value = React.findDOMNode(this.refs.text).value;
// do something with the value
},
render: function () {
return (
<form onSubmit={this.handleSubmit}>
<RichInput />
<input type="submit" value="Save"/>
</form>
);
}
}
Now I am wondering. After looking through available resources, I found the following approach to be used to overcome this limitation:
var RichInput = React.createClass({
render: function() {
return (
<div className="someStyle">
<input type="text" value="foo" onChange={this.props.callback} />
</div>
);
}
}
React.createClass({
handleSubmit: function (event) {
event.preventDefault();
var value = this.state.text
// do something with the value
},
getInitialState() {
return {text: 'foo'};
}
updateText: function(value) {
this.setState({text: value});
}
render: function () {
return (
<form onSubmit={this.handleSubmit}>
<RichInput callback={this.updateText} />
<input type="submit" value="Save"/>
</form>
);
}
}
Is this the canonical solution to writing modularized form components? I am wondering as this does seem like a lot of overhead to me. I need to write extra functions and I need to make the component state-full what drives me a bit away from adapting this solution. Also, I wonder about performance as I really do not need to update the value on every change but only on (and in case of) form submission.
One possibility I found was to use:
React.findDOMNode(this.refs.rich.refs.text);
given that the RichInput has ref="rich" defined on it. But then again, the documentation of React says that refs should not be considered puplic API and be accessed outside of a component.
This is the way I built my abstract Input component. I use it for various purposes (whenever I require the user to input something and I want to handle the action later) (example with ES6/7 with some bootstrap styling):
import React, { PropTypes, Component } from 'react';
export default class Input extends Component {
static propTypes = {
placeholder: PropTypes.string,
buttonText: PropTypes.string,
onButtonClick: PropTypes.func
}
constructor() {
super();
this._handleClick = this._handleClick.bind(this);
this._handleKeyUp = this._handleKeyUp.bind(this);
}
render() {
return (
<div className='Input'>
<div className='input-group'>
<input type='text' className='form-control' placeholder={this.props.placeholder}
ref='inputBox' onKeyUp={this._handleKeyUp}
/>
<span className='input-group-btn'>
<form onSubmit={this._handleClick}>
<button className='btn btn-success' type='submit'>{this.props.buttonText}</button>
</form>
</span>
</div>
</div>
);
}
_handleClick(e) {
e.preventDefault();
let value = this.refs.inputBox.getDOMNode().value;
this.props.onButtonClick(value);
value = null;
}
_handleKeyUp(e) {
e.preventDefault();
if (e.keyCode === 13) {
this._handleClick(e);
}
}
}
And then in the Parent component you can initialize it like:
<Input placeholder='Enter something'
buttonText='Submit'
onButtonClick={this._handleButtonClick}
/>
and handle the _handleButtonClick:
_handleMakeSearch(text) {
console.log(text);
}
It is a commonly solution to create very small components and one wrapper (parent component) that handles all states of his subcomponents, because the states of these subcomponents often depends on the state of other subcomponents.
So you are right, the wrapper/parent component will have a (lot of) overhead, but this way your "real" components are more modular.
var RichInput = React.createClass({
render() {
return (
<div className="someStyle">
<input type="text" value={this.props.value} onChange={this.props.onChange} />
</div>
);
}
}
React.createClass({
handleSubmit: function (event) {
event.preventDefault();
var value = this.state.value;
// do something with the value
},
getInitialState() {
return {value: 'foo'};
}
updateValue(value) {
this.setState({value});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<RichInput value={this.state.value} onChange={this.updateValue} />
<input type="submit" value="Save"/>
</form>
);
}
}
Here is an other question/answer, where you can see an example/use case. A parent component that handles all states of his subcomponents, where each state depends on each other. Maybe it helps to understand the benefits of this approach.