Input text inside a react component - javascript

I'm trying to create an input text inside a react component and then I realised that it's a bad praxis. So I investigated a little bit so I found Controlled-Components, so I think this is what I need, but looking at my Component I do not know how to create it.
I do not have an extends Redux.Component so a friend suggested me to create a Component but couldn't get succeed.
What I was trying is this :
Inside my component
<input
...
/>
{" "}
<input
...
/>
<span>
<myButton
...
arguments={[document.getElementById("id1").value, document.getElementById("id2").value]}
>
[ send ]
</myButton>{" "}
</span>
But I'm getting this error :
The given id must not be null!; nested exception is java.lang.IllegalArgumentException: The given id must not be null!
EDIT
On my component where I have all of those code I have this :
<myButton
id={id}
arguments={[intputStuff]}
>
So my problem is if I do what Tom's says I do not have the id in the other component.
So the thing should be create this component inside the other component and then get the values of the inputtexts and put them as an arguments

It's not clear from your post what exactly you're trying to accomplish.
It appears that you're trying to build a component with 2 text inputs and a button.
If you want the button to "submit" the values of the two inputs, you should do something like this:
class SomeComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
value1: props.initialValue1,
value2: props.initialValue2
}
}
onChangeText = (event) => this.setState({ [event.target.name]: event.target.value })
onClickSubmit = (event) => {
let { onSubmit } = this.props
if(typeof onSubmit !== 'function') return
let { value1, value2 } = this.state
return onSubmit([ value1, value2 ])
}
render() {
let {
initialValue1,
initialValue2,
onSubmit,
className,
...props
} = this.props
let {
value1,
value2
} = this.state
return (
<div className={`SomeComponent ${className}`} {...props}>
<input value={value1} name="value1" onChange={this.onChangeText} />
<input value={value2} name="value2" onChange={this.onChangeText} />
<button onClick={this.onClickSubmit}>
Submit
</button>
</div>
)
}
}
A few notes:
This example uses a bunch of futuristic JS: destructuring, rest/spread, class properties, computed property names, and arrow functions. Each feature is being leveraged for a specific purpose, not just because they're cool. If your environment doesn't support some of these features, you'll need to find a workaround that makes good on some additional constraints.
This is not a controlled component, but it does contain 2 controlled inputs. It uses the "initialValue" pattern: the owning component provides starting values, but is unaware of the blow-by-blow as the user types each character. The owning component is only notified of the new values when the button is clicked. This pattern can result in loss of data if the owner is re-rendered before the current value are submitted.
Generally, when using React, you want to avoid using native DOM methods to access or manipulate elements. (There are plenty of exceptions, of course.) One reason you want to avoid native DOM methods is that component lifecycle methods might execute before the React renderer has actually updated the DOM -- so document.getElementById('someid') might return undefined.

Related

Why an input value should receive the state in react? [duplicate]

I cannot understand why we set the value={this.state.task} when it is just an empty string, and how exactly the flow of data goes from the input value and then to the state.
When we first set the value, it's basically an empty string. But when I try to actually set value='' , the input field does not function properly on the rendered page.
I get that onChange we set the state to the corresponding name and value, and that that's how the data is flowing into the state. But then why does it not work when, again, we just set value='' ?
import React, { Component } from 'react';
import uuid from 'uuid/v4';
export class NewTodoForm extends Component {
constructor(props) {
super(props)
this.state = {
task: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e){
this.setState({
[e.target.name]: e.target.value
})
}
handleSubmit(e){
e.preventDefault();
this.props.createTodo({ ...this.state, id: uuid() });
this.setState({ task: "" });
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label htmlFor='task'>New Todo</label>
<input
type='text'
placeholder='New Todo'
id='task'
name='task'
// why is this {this,state.task} ?
value={this.state.task}
onChange={this.handleChange}
/>
<button>Add Todo</button>
</form>
</div>
)
}
}
export default NewTodoForm
Because value is setting ... well the value of the input. By doing this value={this.state.task} basically you are connecting your input with the component's state and with the lifecycle of the React component. So basically whenever you change your component's state that has the input from anywhere (even programmatically), React will be able to update the input correctly and there won't be any bugs or weird stuff happening.
In the React docs it is explained very well. They are doing this controlled component ...
An input form element whose value is controlled by React in this way is called a “controlled component”.
... so that the state of the React component to be the only 'source of truth', meaning to prevent weird bugs and undesired behaviour.
Since the value attribute is set on our form element, the displayed value will always be this.state.value, making the React state the source of truth.
It is always a good practice to have one source of truth and not many. In this case if you leave the input's value to be different from the component's state, you are making more than one source of truths and thus exposing your app to bugs.

Obtain value from InputText using React.createRef()

Instead of re-rendering the entire component-tree whenever "<InputText style{...}>" is changed, I am trying to use refs in my Class Component. (I am using React Native with Expo managed workflow.)
Using refs, the typed text appears as it should in the InputText field.
But, when a button is pressed, the value of the typed text (value of the InputText) should be console logged, however it is not.
export class Feed extends Component {
constructor(props){
super(props);
this.state = {
//some state variables
}
this.myTextFromInput = React.createRef()
}
I started by creating the myTextFromInput ref (above).
<TextInput
style={{height:100}}
ref={this.alias}
placeholder="Input Text Here"
/>
I then used the myTextFromInput ref in the InputText component. And lastly, the button!
<Button onPress={()=>console.log(this.myTextFromInput.current.value)}>Press me!</Button>
This gives me undefined. I have also tried this.myTextFromInput.value and a .getText() method which is outdated.
How can I obtain the inputed text?
UPDATE:
Terminal log undefined. But snack works fine!?
You aren't passing the correct reference to TextInput, it should be this.myTextFromInput not this.alias, take a look:
export class Feed extends Component {
constructor(props){
super(props);
this.state = {
//some state variables
}
this.myTextFromInput = React.createRef()
// bind the method to the component instance
this.logInputText = this.logInputText.bind(this)
}
logInputText() {
console.log(this.myTextFromInput.current.value)
}
render() {
return (
<View>
<TextInput
style={{height:100}}
// pass the correct reference here
ref={this.myTextFromInput}
placeholder="Input Text Here"
/>
<Button
onPress={this.logInputText}>
Press me!
</Button>
</View>
)
}
}
Also don't forget that whether you use a method instead of arrow function you've to bind it to the class instance, like I did. See this question and this example from react docs.
Update: React.ref on Android and iOS doesn't seems to work as the same way as it works on web, you can't get the value from input because the component doesn't provide this property, doing a console.log(this.myTextFromInput.current) you can see all the available properties. One solution from this question is to use TextInput from the package react-native-paper, as it provides the input value from the ref, or you could use the common state approach to store the input value, like so:
export class Feed extends Component {
constructor(props){
super(props);
this.state = {
myTextFromInput: ""
}
// bind the method to the component instance
this.logInputText = this.logInputText.bind(this)
}
logInputText() {
console.log(this.state.myTextFromInput)
}
render() {
return (
<View>
<TextInput
style={{height:100}}
// you don't need to use ref
placeholder="Input Text Here"
onChangeText={(text) => this.setState({myTextFromInput: text})}
/>
<Button
onPress={this.logInputText}>
Press me!
</Button>
</View>
)
}
}

Submit form on prop value changing

I am new to React and trying to modify this application: https://github.com/nice-table/bitmex-scaled-orders
My goal is, say the prop "instrumentData" found in "src/modules/orders/OrderForm.js" has "instrumentData.lastprice" value changing to a specific value in real-time in the backend. I want to submit the form on that page if the value reaches a specific value. In other words, I want to keep monitoring that prop untill it hits a number and it will submit the form upon that. Is that doable through states? I tried to research it but given I am new to React I am a bit lost as to what code to use and where exactly to add it.
Thanks.
Autosubmitting is simple
It's simple to run some action on data change. React components are data driven - autoupdating. You can just insert a function 'into data flow'.
Your data source is in DataContext then you should use <DataContext.Consumer /> to get data 'stream' - stream because it's frequently updated using socket connection.
<DataContext.Consumer>
{ (data, submitForm, isSubmitting) => {
console.log("context data", data );
// extract data from `data` object
// const someData = data.someProperty;
// if( someData > 12345 ) {
// if( !isSubmitting ) {
// submitForm()
// }
// return "Limit reached"
// }
// return null
}}
</ DataContext.Consumer>
This snippet can be placed almost anywhere after this code:
render={({
values,
errors,
touched,
setFieldValue,
submitForm,
isSubmitting,
isValid,
validateForm
}) => (
<React.Fragment>
// place it here
... and of course before end of this fragment (</ React.Fragment>).
You can pass and use almost all functions defined in this component (file), f.e. setFieldValue("priceUpper", to update form value before submitting.
Autosubmitting is NOT so simple
Problem is not trivial. You should create a component with internal logic to:
set limit (render input, onChange handler, useState) instead of hardcoded value
block(or not? checkbox?) autosubmitting in a loop (formik will submit but later it will clear isSubmitting flag - our component will autosubmit again)
render context consumer inside - optimize rerenderings
etc.
Good luck ;)
React has a really good write up on forms and handling onChange() which is an event that can fire when a field is changed https://reactjs.org/docs/forms.html.
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
}
submit = () => {
// submit form
// eg axios.post(URL, {
// value: this.state.value
// })
}
handleChange(event) {
this.setState({value: event.target.value});
if (this.state.value == 10) {
this.submit()
}
}
return (
<form>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
</form>
);
}
}
I see the github repo is using hooks in lieu of component classes.
You can read about hooks here https://reactjs.org/docs/hooks-intro.html.
onChange can still call handle change and instead of this.state you may be using useState

In React, how do a pass a parameter from a child component up to a parent component?

In React JS, I am having trouble passing a value from a child component up to the parent component
this question is a follow-up to previous question In React JS, how do I tell a parent component that something has happened in the child?
here is the code example I am working with...
I have one problems and two questions:
1) although 'label' is in scope (and I can see it has a value in the debugger) in the render method of InfoBox, when it gets passed up to ContainingBox's boxChosen method, it always comes up 'undefined'. what am I doing wrong here?
2) Is there any way to reduce the repetition (DRY this up) when using JSX to construct the InfoBox. In particular, it bothers me that selectBox={this.boxChosen} is repeated for each instance of InfoBox in the JSX
3) is there any common pattern or accepted practice for the naming of the methods in children or parents? In particular, you'll see that this code lets me define one method called selectBox in the child and another one called boxChosen in the parent. This seems arbitrary to me, as I just picked two separate names that didn't collide so as to make it more understandable. But it strikes me as in a larger app you'd want a consistent function naming pattern to identify which methods were 'pass through' methods (in this case, selectBox) simply passing stuff back up to parents. I don't know; just a thought about whether or not there's a naming convention.
import React from 'react';
import styled from 'styled-components'
import './App.css';
const StyledInfoBox = styled.div`
width: 100px;
border: solid 1px green;
padding: 10px;
cursor: pointer;
`
class InfoBox extends React.Component {
constructor({blurb}) {
super()
this.state = {
label: (blurb ? blurb.label : ""),
}
}
render() {
const {label} = this.state
return (
<StyledInfoBox onClick={() => {
const {label} = this.state
// although 'label' is in scope here, it seems to not be
// correctly passed up to selectBox
this.props.selectBox(this.label)
}
} >
{label}
</StyledInfoBox>
)
}
}
class ContainingBox extends React.Component {
boxChosen =(label) => {
// for some reason label comes out as undefined here
console.log("boxChosen.......", label)
}
render() {
return (
<div>
<InfoBox key={1} blurb={{label: "Aenean malesuada lorem"}} selectBox={this.boxChosen} />
<InfoBox key={2} blurb={{label: "Lorem Ipsum dor ameet"}} selectBox={this.boxChosen} />
</div>
)
}
}
function App() {
return (
<div className="App">
<ContainingBox />
</div>
)
}
export default App;
this.label doesn't exist, so it's undefined. Either pass this.state.label, or since youre destructuring the object, just label. Just a minor mistake
render() {
const {label} = this.state
return (
<StyledInfoBox onClick={() => {this.props.selectBox(label)}}>
{label}
</StyledInfoBox>
)
}
You don't need to do the destructuring twice either.
2.
No, you have to pass all the props to each component. No reason you couldn't throw the boxes in a loop though. You can put the labels in an array and iteratively pass everything. Now you only have to actually write it one time.
class ContainingBox extends React.Component {
const boxChosen =(label) => {}
const labels = ['Aenean malesuada lorem', 'Lorem Ipsum dor ameet'];
render() {
return (
<div>
{labels.map((ele, index) => {
<InfoBox
key={index}
blurb={{label: ele}}
selectBox={this.boxChosen}
/>
})}
</div>
)
}
}
Someone else can probably answer this one better than I can. There isn't a true convention besides name them something that makes sense. You're right that you can name them different things, because they're just functions in different scopes. There's nothing special about them. You can name them the same thing, but you don't need to. The best thing you can do is name them something that is "self documenting" where it is being defined.

Modify render function of external React component (with no access)

I have to use a react component that I cannot modify. It's from an external source, due to changes. This could also be a component from a npm package that I import. This is what it looks like, a simple button:
class Button extends React.Component {
// ... more code above
render() {
const { onClick, disabled, children} = this.props;
return (
<button className={this.getClasses()} onClick={onClick} disabled={disabled}>
{this.props.symbol && <Icon symbol={this.props.symbol} />}
{children}
</button>
);
}
}
How can I add some functionality with no access to the file (I can create my own component that extends the button)? For example, I want a type prop in there. I thought I can just create a <ButtonExtend onClick={resetState} type="button />.
How can I do this? Ideally I would like to make this even more flexible, so I can also do: <ButtonExtend onClick={resetState} type="submit" name="extended button" />.
I would expect the html to render all the properties from <Button> with my additional html attributes. So I want to use the functionality of the original and my additional props. Or it this not even possible, to change the render method of another component, if the component doesn't make it possible?
Although public methods and properties of a component are accessible by refs (https://reactjs.org/docs/refs-and-the-dom.html) the pattern are you looking for is High Order Components (HOC, https://reactjs.org/docs/higher-order-components.html)
Unless a component was designed for customization, there is no straightforward way to do this.
Button is an example of badly designed component because it doesn't accept additional props. An issue and PR could be submitted to the repository in order to address original problem.
In extended component, this can be fixed by passing props from extended component.
Parent render result could be modified:
class ButtonExtend extends Button {
// ... more code above
render() {
const button = super.render();
const { symbol, children, ...props } = this.props;
return React.cloneElement(button, {
children: [
symbol && <Icon symbol={symbol} />,
...children
],
...props
});
}
If an element that needs to be modified is nested, this may become messy and result in unnecessarily created elements.
A cleaner way is to paste render in extended component and modify it:
class ButtonExtend extends Button {
// ... more code above
render() {
const { symbol, children, ...props } = this.props;
return (
<button className={this.getClasses()} {...props}/>
{symbol && <Icon symbol={symbol} />}
{children}
</button>
)
}
}
This way it can be used as
<ButtonExtend onClick={resetState} type="submit" name="extended button" />

Categories