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

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.

Related

why i can not use passed refs in input element

I am trying to focus on error fields inside of a form and I am using ref to do that but somehow ref always returns null and I am getting errors, I want to use/pass this ref to focus on error field , here error is stored in state which i have not included in code
here is the basic code,
class Form extends Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
this.getErrors = this.getErrors.bind(this);
}
componentWillUnmount() {
/////
}
renderSubmitAction() {
if(errors) {
this.textInput.current.focus();
}
}
render() {
return (
<View key={propertyName}>
<FormInput
id={id_prefix + propertyName}
key={propertyName}
ref={this.textInput}
/>
</View>
)
const submitButton = this.renderSubmitAction();
return (
<View key={this.state.submitId} style={props.style}>
{children}
{props.children}
{submitButton}
</View>
);
}
Problem
You are running this:
this.textInput.current.focus();
before this happens:
<FormInput
id={id_prefix + propertyName}
key={propertyName}
ref={this.textInput}
/>
Solution
I'm not sure what this is supposed to do:
const submitButton = this.renderSubmitAction();
But you probably wanted to use this:
const submitButton = this.renderSubmitAction;

how to avoid re-rendering when passing the object into value of context.provider using React context

In class component when we want to pass the object to the value of context provider using react context, we have a way to avoid re-rendering issue. Below are the codes
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "",
contextState: {
count: 0,
increment: this.increment
}
};
}
increment = () => {
this.setState({
contextState: {
...this.state.contextState,
count: this.state.contextState.count + 1
}
});
};
onChange = e => {
const { value, name } = e.target;
this.setState({ [name]: value });
};
render() {
return (
<CountContext.Provider value={this.state.contextState}>
<div style={styles}>
<input name="text" value={this.state.text} onChange={this.onChange} />
<div>Count: {this.state.contextState.count}</div>
<Container1 />
<Container2 />
</div>
</CountContext.Provider>
);
}
}
We put this.state.contextState to value of CountContext.Provider. So when user types anything in input element and will not cause <Container1 /> and <Container2 /> re-rendered. Here is the code sandbox: https://codesandbox.io/s/qqx1jqk8mj?file=/src/index.js:260-1105
I am tring to convert it into hooks. Here is the code sandbox https://codesandbox.io/s/affectionate-gauss-duk64?file=/src/index.js but the counter is not working properly. May I please know which part is wrong? thanks
In your hook component, you just need to use the functional setState approach.
setContextState(prevState=>newState)
In your code: https://codesandbox.io/s/admiring-shtern-g6oll?file=/src/index.js
const [contextState, setContextState] = useState({
count: 0,
increment: () => {
setContextState(prev=>({
...prev,
count: prev.count + 1
}));
}
});
The reason you need to do this is because the state value will never update because of the closure around it. contextState.count will always remain at 0 because it was the value when the state was originally set (0), and it won't change.

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

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.

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.

NativeBase multiple icons in InputGroup

I'm trying to make an InputGroup that has two icons OR one icon and one button.
One icon should be used to check if the input field is empty or not (got that working). The other icon OR button should be used to "inject" some text into the Input field.
Currently my code looks like this:
import React, { Component } from 'react'
import { Content, List, InputGroup, Input, Icon, Button } from 'native-base'
export default class AddEquipment extends Component {
constructor(props) {
super(props)
this.state = {
parameters: {
one: {value:"", hint: "One"},
two: {value:"", hint: "Two"},
three: {value:"Valid", hint: "Three"}
}
}
this.updateParameter = this.updateParameter.bind(this)
this.validationStyle = this.validationStyle.bind(this)
}
componentDidMount() {
}
updateParameter(key, value) {
newState = {...this.state}
newState.parameters[key].value = value
this.setState = newState;
}
validationStyle(text) {
color = text === "" ? "#b03939" : "#649370"
return (
{ marginRight:25, color
}
)
}
render () {
return (
<Content>
{ Object
.keys(this.state.parameters)
.map( key =>
<InputGroup
key={`${key}_InputGroup`}
iconRight
borderType='regular'
style={{margin:5}}
>
<Input
placeholder={this.state.parameters[key].hint}
onChangeText={(text) => {
console.log(this.state.parameters)
this.updateParameter(key, text)} }
value={key.value}
/>
<Icon
key={`${key}_validIcon`}
name={ this.state.parameters[key].value === "" ? 'ios-alert' : 'ios-checkmark-circle'}
style={ this.validationStyle(this.state.parameters[key].value) }
/>
<Icon
key={`${key}_injectNA`}
name='ios-beer'
onPress={() => this.updateParameter(key, "Cheers!") }/>
</InputGroup>
)
}
</Content>
)
}
}
Which gives me the following result
First issue
As you can see I have trouble getting the other icon appear - it doesn't seem to lie behind the first.
A button would be as good, but it always drops below the Input and not next to it. Styling is not my strongest style - hence why I use the awesome framework NativeBase
Second issue
Another problem I have is that the validation does not seem to change icon and color after the state is being updated. Seems like the style are loaded only once.
on the first issue would you consider validity icon in front? InputGroup can dispay maximum two icons: before and after text input, see code below.
on the second issue it's caused that you assign value to the state, while in React you should call this.setState(newState), see reference
another issue is accessing to the object key's value by key.value, while key is just a string (key name), while real object accessed by this.state.parameters[key]
Following code fixes all mentioned issues (but validity icon is on the left):
import React, { Component } from 'react'
import { Content, List, InputGroup, Input, Icon, Button } from 'native-base'
const errorStyle = {color: "#b03939"}
const validStyle = {color: "#649370"}
export default class AddEquipment extends Component {
constructor(props) {
super(props)
this.state = {
parameters: {
one: {value:"", hint: "One"},
two: {value:"", hint: "Two"},
three: {value:"Valid", hint: "Three"}
}
}
this.updateParameter = this.updateParameter.bind(this)
}
componentDidMount() {
}
updateParameter(key, value) {
let newState = {...this.state}
newState.parameters[key].value = value
this.setState(newState);
}
static validationStyle(text) {
return text === "" ? errorStyle : validStyle;
}
render () {
return (
<Content>
{ Object
.keys(this.state.parameters)
.map( key =>
<InputGroup
key={`${key}_InputGroup`}
borderType='regular'
style={{margin:5}}
>
<Icon
key={`${key}_validIcon`}
name={ this.state.parameters[key].value === "" ? 'ios-alert' : 'ios-checkmark-circle'}
style={ AddEquipment.validationStyle(this.state.parameters[key].value) }
/>
<Input
placeholder={this.state.parameters[key].hint}
onChangeText={(text) => {
console.log(this.state.parameters)
this.updateParameter(key, text)} }
value={this.state.parameters[key].value}
/>
<Icon
key={`${key}_injectNA`}
name='ios-beer'
onPress={() => this.updateParameter(key, "Cheers!") }/>
</InputGroup>
)
}
</Content>
)
}
}

Resources