Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 24 additions & 25 deletions src/components/TextInput/BaseTextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import useNativeDriver from '../../libs/useNativeDriver';
import * as Browser from '../../libs/Browser';

function BaseTextInput(props) {
const inputValue = props.value || props.defaultValue || '';
const initialActiveLabel = props.forceActiveLabel || inputValue.length > 0 || Boolean(props.prefixCharacter);
const initialValue = props.value || props.defaultValue || '';
const initialActiveLabel = props.forceActiveLabel || initialValue.length > 0 || Boolean(props.prefixCharacter);

const [isFocused, setIsFocused] = useState(false);
const [passwordHidden, setPasswordHidden] = useState(props.secureTextEntry);
Expand Down Expand Up @@ -145,30 +145,16 @@ function BaseTextInput(props) {
[props.autoGrowHeight, props.multiline],
);

useEffect(() => {
// Handle side effects when the value gets changed programatically from the outside

// In some cases, When the value prop is empty, it is not properly updated on the TextInput due to its uncontrolled nature, thus manually clearing the TextInput.
if (inputValue === '') {
input.current.clear();
}

if (inputValue) {
activateLabel();
}
}, [activateLabel, inputValue]);

// We capture whether the input has a value or not in a ref.
// It gets updated when the text gets changed.
const hasValueRef = useRef(inputValue.length > 0);
// The ref is needed when the component is uncontrolled and we don't have a value prop
const hasValueRef = useRef(initialValue.length > 0);
const inputValue = props.value || '';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the difference between initialValue and inputValue and their purpose? Unable to understand clearly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initialValue is the value that we have when the component got created for the first time.
inputValue is the value prop but with the fallback to an empty string (instead of undefined)

const hasValue = inputValue.length > 0 || hasValueRef.current;

// Activate or deactivate the label when the focus changes:
// Activate or deactivate the label when either focus changes, or for controlled
// components when the value prop changes:
useEffect(() => {
// We can't use inputValue here directly, as it might contain
// the defaultValue, which doesn't get updated when the text changes.
// We can't use props.value either, as it might be undefined.
if (
hasValueRef.current ||
hasValue ||
isFocused ||
// If the text has been supplied by Chrome autofill, the value state is not synced with the value
// as Chrome doesn't trigger a change event. When there is autofill text, keep the label activated.
Expand All @@ -178,7 +164,16 @@ function BaseTextInput(props) {
} else {
deactivateLabel();
}
}, [activateLabel, deactivateLabel, inputValue, isFocused]);
}, [activateLabel, deactivateLabel, hasValue, isFocused]);

// When the value prop gets cleared externally, we need to keep the ref in sync:
useEffect(() => {
// Return early when component uncontrolled, or we still have a value
if (props.value === undefined || !_.isEmpty(props.value)) {
return;
}
hasValueRef.current = false;
}, [props.value]);

/**
* Set Value & activateLabel
Expand All @@ -192,9 +187,13 @@ function BaseTextInput(props) {
}

Str.result(props.onChangeText, value);

if (value && value.length > 0) {
hasValueRef.current = true;
activateLabel();
// When the componment is uncontrolled, we need to manually activate the label:
if (props.value === undefined) {
activateLabel();
}
} else {
hasValueRef.current = false;
}
Expand Down