How can I use single ref with several TextInput (react-native) - reactjs

I want to unfocus all of my TextInput`s when I click on TouhableOpacity.
I have learned that I can use refs for achieve that. But when I use single ref with several TextInputs I got that behaviour with only last TextInput
class UserRegister extends React.Component<any, State> {
private inputRef: React.RefObject<TextInput>;
constructor(props:any) {
super(props);
//some code
this.inputRef = React.createRef();
}
onSwitchPicker = () => {
if (this.inputRef.current) {
this.inputRef.current.blur();
}
//some code
}
render() {
return (
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<TextInput
ref={this.inputRef}
//other params
/>
<TextInput
ref={this.inputRef}
//other params
/>
<TouchableOpacity
onPress={this.onSwitchPicker}
//other params
>
//some code
</TouchableOpacity>
// some code
</KeyboardAvoidingView>
);
}
}

If at least one input exists anyway(is not conditionally rendered), then you can set it focused and blur right next line using just single createRef().
onSwitchPicker = () => {
this.specialInputRef.current.focus();
this.specialInputRef.current.blur();
}
Not sure if that would scroll screen in React Native or not. If it scrolls - and this is definitely bad user experience - you can have N refs:
onSwitchPicker = () => {
this.inputRef1.current.blur();
this.inputRef2.current.blur();
this.inputRef3.current.blur();
this.inputRef4.current.blur();
}
The only way I see how to make it shorter is to have array of refs:
constructor(props) {
super(props);
this.inputRefs = new Array(10).fill().map(() => React.createRef());
}
onSwitchPicker = () => {
this.inputRefs.forEach(({ current }) => {
if (current) current.blur();
});
}
render() {
....
<input ref={this.inputRefs[0]} ...
...
<input ref={this.inputRefs[1]} ...
...
<input ref={this.inputRefs[11]} ...
....
But if there is conditional rendering I'd rather have boilerplate code instead of array. It's possible to have hard-to-catch bugs if used the same index to 2 inputs by mistake:
{someCondition && <input ref={this.inputRefs[4]} ...}
....
{someOtherCondition && <input ref={this.inputRefs[4]} ...}
Then sometimes your user will experience cases when focus stays in the field(I still don't know what's the point but guess it should affect UI in some way). But it might be hard to reproduce if user does not catch all the conditions.

Related

React wrong & right practices with form elements

Hyall
Can you please point out bad practices / mistakes in the code below?
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "default title"
};
this.inputTxt = this.state.title;
this.myRef = React.createRef();
}
componentDidMount() {
this.myRef.current.value = this.inputTxt;
}
handleSubmit = e => {
e.preventDefault();
console.log("submitted");
this.setState({ ...this.state, title: this.inputTxt });
};
handleInput = e => {
this.inputTxt = e.target.value;
};
render() {
return (
<>
<div>{this.state.title}</div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
onChange={this.handleInput}
ref={this.myRef}
></input>
<button type="submit">Save</button>
<button type="reset">Reset</button>
</form>
</>
);
}
}
And some special questions:
is it ok to use this.somevar properties of component class to store variables' values? how to avoid naming collisions?
is it normal to use refs to set input's value?
if I want to set onChange and value bound to reactive variable in one input control, it will freeze? how to gain [(ngModel)] Angular-like control over input element?
It seems like you're over complicating things. I don't see a need for refs here. I don't think setting a class property will trigger a re-render, so this way of managing input might not work at all regardless of it not being a best practice.
Just use state as the value, and update state on change. To keep things flexible, use the input's name as the state key. Something like this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "default title"
};
}
handleSubmit = e => {
e.preventDefault();
console.log("submitted");
// Not sure if thats what you're looking for..
// Also: no need to do {...this.state, }. setState does a merge, not overwrite
this.setState({ title: this.state.input1 });
};
handleChange = e => {
// Use e.target.name as the computed property name,
// so it can be used for infinite number of inputs
this.setState({[e.target.name]: e.target.value});
};
render() {
return (
<>
<div>{this.state.title}</div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
name="input1" // Give it a unique name for setting state
value={this.state.input1} // Specify the value instead of using a ref
onChange={this.handleChange}
></input>
<button type="submit">Save</button>
<button type="reset">Reset</button>
</form>
</>
);
}
}
Here is the link to the react docs on refs.
The primary they recommend use-cases are:
Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
Which I don't believe apply, here. So I wouldn't recommend using them here.

Migration from componentWillReceiveProps to getDerivedStateFromProps

I am learning reactjs and I wrote component with the method componentWillReceiveProps (cWRP) but I read that it is deprecated and it must replace with getDerivedStateFromProps (gDSFP) - https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html.
Please note that the following code has the sole purpose of illustrating my problem and questions. It is not a full code.
App.js file :
import React from 'react';
import './App.css';
import Display from './component.js'
class App extends React.Component {
state={resetCounter:false}
resetCounter= () => this.setState( {resetCounter: true} );
render() {
return (
<div className="App">
<header className="App-header">
<Display resetCounter={this.state.resetCounter}></Display>
<div>
<p></p><p></p>
<button onClick={this.resetCounter}>Reset</button>
</div>
</header>
</div>
);
}
componentDidUpdate () {
if (this.state.resetCounter!==false)
this.setState( {resetCounter: false} );
}
}
export default App;
component.js file
import React from 'react'
class Display extends React.Component {
constructor() {
super();
this.state = this.resetState();
this.state.generalCounter=0;
}
/* method to avoid code duplication in constructor and cWRP
could not be used with getDerivedStateFromProps */
resetState = () => ({resettableCounter: 0,});
componentWillReceiveProps(nextProps) {
if (nextProps.resetCounter===true)
this.setState(this.resetState())
}
render() {
return (
<>
<div>
<div>general counter : {this.state.generalCounter}</div>
<div>resettable counter : {this.state.resettableCounter}</div>
</div>
<div>
<button onClick={this.incCounters}>+</button>
<button onClick={this.decCounters}>-</button>
</div>
</>
)
}
incCounters= () => this.setState(
{
resettableCounter: this.state.resettableCounter+1,
generalCounter: this.state.generalCounter+1
}
)
decCounters= () => this.setState(
{
resettableCounter: this.state.resettableCounter-1,
generalCounter: this.state.generalCounter-1
}
)
}
export default Display
In the state of the component, there is a resettable part and a non resettable one. A method resetState is used to avoid code duplication in the constructor and in cWRP.
To replace cWRP by gDSFP, I wrote a class method because instance method could NOT be called in gDSFP (this is not usable)
...
constructor() {
super();
this.state = Display.resetState();
this.state.generalCounter=0;
}
static resetState () {
return ({resettableCounter: 0,});
}
static getDerivedStateFromProps(nextProps) {
if (nextProps.resetCounter === true) {
return Display.resetState();
} else {
return null;
}
}
...
With this solution, it is very easy to modify all my components but I am not sure that it is a good mean.
I wonder if I have a misconception and if I should rewrite my components to separate them into Fully controlled components and Fully uncontrolled components with a key ( https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#preferred-solutions).
For example, in this case, do I have to write :
One Fully uncontrolled components for the resettable counter
One Fully controlled one for the non resettable counter
A parent component with the +/- buttons to render them.
I ask this question because in some cases, it will be much work, so I want to be sure before continuing.
You would want to keep the gdsfp version in your post if your component depends on some outside props, which you don't have controll over (such as JSON returned or 3rd party render props component, etc).
It looks like you have a full control over what's passed down to the Display. You can pass down an initial resettableCounter value down to Display.
The advantage is two-folds.
Your Display props shows what the Display does - Making it more descriptivie/readable.
It's easier to maintain, as you don't have to massage the data.
For your particular case, Fully uncontrolled component with a key seems to make more sense, as Display should accept the initial value to show, but is responsible for managing the reseetableCounter.
Unless it's absolutely unavoidable, don't create components which control their siblings (or parents). Instead, lift state to a common ancestor:
const Display = ({
generalCounter,
resettableCounter,
incrementCounters,
decrementCounters,
}) => (
<div>
<div>General Counter: {generalCounter}</div>
<div>Resettable Counter: {resettableCounter}</div>
<button onClick={incrementCounters}>Increment</button>
<button onClick={decrementCounters}>Decrement</button>
</div>
);
class DisplayContainer extends React.Component {
state = {
generalCounter: 0,
resettableCounter: 0,
};
incrementCounters = () => this.setState(prevState => ({
generalCounter: prevState.generalCounter + 1,
resettableCounter: prevState.resettableCounter + 1,
}));
decrementCounters = () => this.setState(prevState => ({
generalCounter: prevState.generalCounter - 1,
resettableCounter: prevState.resettableCounter - 1,
}));
resetResettableCounter = () => this.setState({
resettableCounter: 0,
});
render() {
return (
<React.Fragment>
<Display
{...this.state}
incrementCounters={this.incrementCounters}
decrementCounters={this.decrementCounters}
/>
<button onClick={this.resetResettableCounter}>
Reset Resettable Counter
</button>
</React.Fragment>
);
}
}
const App = () => (
<div>
<DisplayContainer />
</div>
);
An alternative approach would be something like Redux (which effectively lifts state out of React).

How to use props of a component out of the component in react with typescript?

As i am new to react i have a question.I have a react component and its properties. And I want to reach one of these properties from the page where i used my component.
type BranchProps = {
SelectedBranch : string
}
class Branch extends React.Component<BranchProps, BranchState> {
constructor(props: BranchProps) {
super(props);
}
render() {
return (
<SelectBox></SelectBox>
)
}
}
export default Branch ;
ParentPage.tsx
import Branch...
class Page extends.... {
ctor..
const test:string = () => {
Branch.SelectedBranch ???
}
}
And i want to get "SelectedBranch" from my ParentPage.
Note: SelectedBranch is changing on change event. Should i make my SelectedBranch a const and export it or what should i do ?
I have created this Input.js child component with different props
const Input = ({ placeholder, label, value, onChangeText, secureTextEntry }) => {
return (
<View >
<Text >{ label }</Text>
<TextInput
secureTextEntry={secureTextEntry}
placeholder={placeholder}
autoCorrect={false}
value={value}
onChangeText={onChangeText}
style={inputStyles}
/>
</View>
);
};
Once I import it to be used on a page, this is how the manipulation of the content is being done. the value is been passed on by simply quoting the specific prop
<Input
secureTextEntry
placeholder={'password'}
label={'Password'}
value={this.state.password}
onChangeText={password => this.setState({ password })}
/>
Here the 'password' is been assigned to the component by using the state of the parent. something like this, you can assign the value as you see fit.
state = { email: '', password: '', error: '', loading: false };
A far better way exist by using the Redux approach. would be advisable to have a look.
Firstly, you should understand the difference between state and props inside a component. Props shouldn't be updated, it's the state's role.
You can't directly access component's props outside of it.
In pure react (without librabry like redux) the right way should be to use callbacks to return the element to the parent.
class Branch extends React.Component<BranchProps, BranchState> {
state = {
'selectedBranch': ''
}
constructor(props: BranchProps) {
super(props);
}
handleOnChange = (e) => {
this.setState({'selectedBranch': e.target.value})
this.props.parentHandleChangeBranch(this.state.selectedBranch);
}
render() {
return (
<SelectBox value={this.state.selectedBranch} onChange="{this.handleOnChange}"></SelectBox>
)
}
}
class Page extends React.Component {
state = {
'branch': null
}
parentHandleChangeBranch = (branch) => {
this.setState({'branch': branch};
}
render () {
<div>
<Branch parentHandleChangeBranch={this.parentHandleChangeBranch} />
</div>
}
}
You can declare a function in the parent component and pass it as prop to the child. Then, call this callback whenever you want inside the child.

React Native binding functions over .map()

So I am having some trouble combining concepts of .map() and function binding. I am using .map() in the same way ngFor is used in angular, to place a custom button component on the page for every item in a user's account.
Here is some example code:
class MyButton extends Component {
constructor(props) {
super();
this.state = {
progress: 0
}
}
render() {
return(
<TouchableWithoutFeedback onPress={this.pressFunction}>
(...more code inside)
</TouchableWithoutFeedback>
)
}
pressFunction = () => {
(animate progress from 0 to 1 for some animation)
}
}
/////////////////////////////////////////////////////////////////
class Parent extends Component {
render() {
return(
{
this.props.data.array.map(obj => {
return(
<View style={someStyle}>
<MyButton data={obj} />
</View>
)
})
}
)
}
}
So in the Parent Component, multiple MyButtons are rendered properly, each according to the passed object from the array. However, when any button is pressed, all of the pressFunctions for all MyButtons fire.
My question is I guess, how do I ensure that each pressFunction of each MyButton is bound only to the specific instance of the MyButton? I am having trouble with the scope here.
My understanding is that
functionName = () => {}
should properly bind the function to the instance, but I have tried the older ways as well with the same result.
I solved this by creating a dynamic ref on each object mapped to a MyButton, using a unique property of each obj in the array:
this.props.data.array.map(obj => {
return(
<View style={someStyle}>
<MyButton ref={obj.name} data={obj} />
</View>
)
})
Still don't know why my it didn't bind uniquely without a ref
You should pass onPress as a props. Below is the updated code
class MyButton extends Component {
constructor(props) {
super();
this.state = {
progress: 0
}
}
render() {
return(
<TouchableWithoutFeedback onPress={this.props.onPress}>
(...more code inside)
</TouchableWithoutFeedback>
)
}
}
/////////////////////////////////////////////////////////////////
class Parent extends Component {
pressFunction = () => {
(animate progress from 0 to 1 for some animation)
}
render() {
return this.props.data.array.map(obj => {
return(
<View style={someStyle}>
<MyButton
data={obj}
onPress={this.pressFunction}
/>
</View>
)
})
}
}

React Native - TextInput - How to use value & defaultValue together

I got the following Component and I want to init TextInput with defaultValue and then when user type update the value of it.
How do I do that?
Here what I've tried - but this way TextInput is always empty on initialization.
class Note extends Component {
state = {
text: ""
};
render() {
const {onChange} = this.props;
return (
<TextInput
onChangeText={(text) => {
this.setState({text});
onChange(text);
}
value={this.state.text}
defaultValue={this.props.text}
/>
);
} }
"react": "^16.4.1"
"react-native": "^0.55.4",
Finally solved it,
The key is the following - "value" has preceding over "defaultValue" unless "value" is undefined!
So, Init this.state.text = undefined, this way defaultValue can be initialized with this.props.text.
class Note extends Component {
state = {
text: undefined
};
render() {
const {onChange} = this.props;
return (
<View>
<TextInput
onChangeText={(text) => {
this.setState({text});
onChange(text);
}
value={this.state.text}
defaultValue={this.props.text}
/>
</View>
);
}
}
I found this a bit more clean than #anilSidhu solution.
You can set default value in state itself like following:
class Note extends Component {
state = {
text: this.props.text
};
render() {
const {onChange} = this.props;
return (
<TextInput
onChangeText={(text) => {
this.setState({text});
onChange(text);
}
value={this.state.text}
/>
);
} }
class Note extends Component {
state = {
text: undefined
};
componentDidMount() {
this.setState({ text: this.props.text })
}
render() {
return (
<TextInput
onChange={(text) => {
this.setState({ text: text.target.value });
}}
value={this.state.text}
/>
);
}
}
It should work for you and if you are still facing the issue let me know please
Basically, when you're having both value and defaultValue props, always value prop will take precedence and thus defaultValue won't be reflected.
Sometimes if you are showing number type to default value it doesn't work. you must convert to string type as follows:
defaultValue(String(this.props.text)}
For someone having trouble with this problem. I've experienced it. i expect my number Value to be displayed on Textinput. But placeholders keep showing.
The solution is your Value must be String.
So i convert my Number to String use toString() method.
Hope this helps.

Resources