Browse code
feature: add demonstration of focus management
Ed Langley authored on 06/10/2019 20:22:03
Showing 4 changed files
Showing 4 changed files
1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,22 @@ |
1 |
+import React from "react"; |
|
2 |
+import PropTypes from "prop-types"; |
|
3 |
+ |
|
4 |
+export class FocusManager extends React.Component { |
|
5 |
+ constructor() { |
|
6 |
+ super(); |
|
7 |
+ this.ref = React.createRef(); |
|
8 |
+ this.doFocus = () => this.ref.current && this.ref.current.focus(); |
|
9 |
+ } |
|
10 |
+ componentDidMount() { |
|
11 |
+ if (this.ref.current) this.ref.current.focus(); |
|
12 |
+ //eslint-disable-next-line no-console |
|
13 |
+ else console.error("ref unbound"); |
|
14 |
+ } |
|
15 |
+ render() { |
|
16 |
+ return this.props.children(this.ref, this.doFocus); |
|
17 |
+ } |
|
18 |
+} |
|
19 |
+ |
|
20 |
+FocusManager.propTypes = { |
|
21 |
+ children: PropTypes.func.isRequired |
|
22 |
+}; |
... | ... |
@@ -1,6 +1,10 @@ |
1 | 1 |
import React from "react"; |
2 | 2 |
import PropTypes from "prop-types"; |
3 | 3 |
|
4 |
+const InputWithFocus = React.forwardRef((props, ref) => ( |
|
5 |
+ <input {...props} ref={ref} /> |
|
6 |
+)); |
|
7 |
+ |
|
4 | 8 |
export class NameControl extends React.Component { |
5 | 9 |
constructor(props) { |
6 | 10 |
super(props); |
... | ... |
@@ -13,7 +17,8 @@ export class NameControl extends React.Component { |
13 | 17 |
if (this.props.name === "") { |
14 | 18 |
return ( |
15 | 19 |
<div> |
16 |
- <input |
|
20 |
+ <InputWithFocus |
|
21 |
+ ref={this.props.focusRef} |
|
17 | 22 |
type="text" |
18 | 23 |
value={this.state.cur_input} |
19 | 24 |
onChange={e => this.setState({ cur_input: e.target.value })} |
... | ... |
@@ -14,6 +14,7 @@ import { rootReducer, updateName } from "./reducer"; |
14 | 14 |
import { rootSaga, getIp, fail } from "./root-saga"; |
15 | 15 |
|
16 | 16 |
import { put } from "redux-saga/effects"; |
17 |
+import { FocusManager } from "./FocusManager"; |
|
17 | 18 |
|
18 | 19 |
// Store Setup ============================================================================== |
19 | 20 |
|
... | ... |
@@ -46,7 +47,9 @@ function* toplevel() { |
46 | 47 |
ReactDOM.render( |
47 | 48 |
// The Toplevel component is wrapped in a provider, to make the store available to connect |
48 | 49 |
<Provider store={store}> |
49 |
- <ConnectedRoot /> |
|
50 |
+ <FocusManager> |
|
51 |
+ {(ref, doFocus) => <ConnectedRoot focusRef={ref} {...{ doFocus }} />} |
|
52 |
+ </FocusManager> |
|
50 | 53 |
</Provider>, |
51 | 54 |
document.getElementById("root"), |
52 | 55 |
resolve |
... | ... |
@@ -3,7 +3,17 @@ import PropTypes from "prop-types"; |
3 | 3 |
import { NameControl } from "./NameControl"; |
4 | 4 |
import { IpControl } from "./IpControl"; |
5 | 5 |
|
6 |
-export const Root = ({ name, updateName, ip, getIp, fail, error, restart }) => ( |
|
6 |
+export const Root = ({ |
|
7 |
+ name, |
|
8 |
+ updateName, |
|
9 |
+ ip, |
|
10 |
+ getIp, |
|
11 |
+ fail, |
|
12 |
+ error, |
|
13 |
+ restart, |
|
14 |
+ doFocus, |
|
15 |
+ focusRef |
|
16 |
+}) => ( |
|
7 | 17 |
<div> |
8 | 18 |
{error ? ( |
9 | 19 |
<div> |
... | ... |
@@ -11,10 +21,11 @@ export const Root = ({ name, updateName, ip, getIp, fail, error, restart }) => ( |
11 | 21 |
error resolved. <button onClick={restart}>Restart</button> |
12 | 22 |
</div> |
13 | 23 |
) : null} |
14 |
- <NameControl name={name} updateName={updateName} /> |
|
24 |
+ <NameControl name={name} updateName={updateName} focusRef={focusRef} /> |
|
15 | 25 |
<IpControl ip={ip} getIp={getIp} /> |
16 | 26 |
<p /> |
17 | 27 |
<button onClick={fail}>Fail</button> |
28 |
+ <button onClick={doFocus}>Focus!</button> |
|
18 | 29 |
</div> |
19 | 30 |
); |
20 | 31 |
Root.propTypes = { |