1import React, { useCallback, useEffect, useMemo, useState } from 'react';
2import cn from 'classnames';
4import { Flex } from 'indigo-react';
6import { compose } from 'lib/lib';
7import { kDefaultValidator } from 'lib/validators';
15 const [value, setValue] = useState(initialValue);
16 const [focused, setFocused] = useState(autoFocus);
17 const [hasBeenFocused, setHasBeenFocused] = useState(false);
20 // TL;DR: This is what we want, the exhaustive deps rule is being too nosy.
22 // Here we memo-construct the `validate` and `transform` functions
23 // based on the prop arguments. We explicitly disable the exhausive deps rule
24 // because eslint wants an inline array specifying our dependencies.
26 // If we were to place the array of validators or transformers into
27 // the dependency array like `[validators]`, we would end up creating a new
28 // `validate` function every render, since deps are shallow-compared with
29 // `Object.is`. Because the array of validators are constructed as part of
30 // a component's render() function like:
32 // export function MnemonicInput(props) {
37 // placeholder={`e.g. ${kExampleMnemonic}`}
38 // validators={[validateMnemonic, validateNotEmpty]}
44 // we create a new array, which shallowly compares as not-equal
47 // So, in this specific case, we disable the rule and pass the
48 // array of validators (and transformers) themselves as dependencies.
49 // This means that validators and transformers can change between renders
50 // and we will re-create the composed `validate` and `transform` functions
53 // For context, an alternative solution I tried was defining the array of
54 // validators as a constant array outside of the render method, but:
55 // 1. It wasn't as clean or useful as specifying them inline,
56 // 2. It would mean that validators could not be updated between renders,
57 // 3. And the situation we have now feels naturally more 'correct'.
59 /* eslint-disable react-hooks/exhaustive-deps */
60 const transform = useMemo(() => compose(...transformers), transformers);
61 const validate = useMemo(
69 /* eslint-enable react-hooks/exhaustive-deps */
71 const onChange = useCallback(
72 e => setValue(transform(e.target.value)), //
75 const onFocus = useCallback(
76 e => setFocused(true), //
79 const onBlur = useCallback(
82 setHasBeenFocused(true);
84 [setFocused, setHasBeenFocused]
87 // memo-compute whether or not the current value is valid
88 const { pass, error: _error, data } = useMemo(
89 () => validate(value), //
93 // only set error if it should be visible
94 const error = !pass && hasBeenFocused && !focused && _error;
95 // visibly tell the user that their input has passed if it has passed
96 // and they are or have interacted with the input before
97 const visiblyPassed = pass && (hasBeenFocused || focused);
108 focused: focused ? 'true' : 'false',
109 // ^ dom wants string value for focused
116export default function Input({
134 const actuallyAutoFocus = autoFocus && !disabled;
135 const { focused, pass, visiblyPassed, error, data, bind } = useInput({
139 autoFocus: actuallyAutoFocus,
142 // notify parent of enter keypress iff not disabled
143 const onKeyPress = useCallback(
144 e => !disabled && e.key === 'Enter' && onEnter && onEnter(),
148 // notify parent of value only when passing
150 pass && onValue && onValue(data);
151 }, [pass, data, onValue]);
153 // notify parent of pass
155 onPass && onPass(pass);
158 // notify parent of error whenever error changes
160 onError && onError(error);
161 }, [error, onError]);
163 // notify parent of focus
165 onFocus && onFocus(focused);
166 }, [focused, onFocus]);
170 <Flex.Item as="label" className="f6 lh-tall" htmlFor={name}>
177 cols={[1, accessory ? 11 : 13]}
182 'bg-white': !disabled,
183 'bg-gray1': disabled,
190 'b-green3': visiblyPassed,
191 'b-black': focused && !visiblyPassed,
192 'b-yellow3': !focused && !visiblyPassed && error,
193 'b-gray3': !focused && !error && !visiblyPassed,
198 ...(disabled && { pointerEvents: 'none', cursor: 'not-allowed' }),
200 onKeyPress={onKeyPress}
202 autoFocus={actuallyAutoFocus}
207 <Flex.Item style={{ height: '45px', overflow: 'hidden' }}>
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list