diff options
| author | haishanh <[email protected]> | 2018-11-05 18:32:16 +0800 |
|---|---|---|
| committer | Haishan <[email protected]> | 2018-11-06 22:37:21 +0800 |
| commit | 91ecdaa5dd1a65ff0ae944896945c0fe4bc23574 (patch) | |
| tree | 9f7f476672fae91eca5df9e0ae021b45df950679 /src/components | |
| parent | 1adaedcbc551f6beee4e9e0b9fbd94197ebdffe5 (diff) | |
update: convert more components to function ones
includes Input, Config, SideBard.
also removed react-redux Provider
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/APIConfig.js | 5 | ||||
| -rw-r--r-- | src/components/Button.js | 31 | ||||
| -rw-r--r-- | src/components/Config.js | 94 | ||||
| -rw-r--r-- | src/components/ContentHeader.js | 26 | ||||
| -rw-r--r-- | src/components/Icon.js | 2 | ||||
| -rw-r--r-- | src/components/Input.js | 65 | ||||
| -rw-r--r-- | src/components/Modal.js | 48 | ||||
| -rw-r--r-- | src/components/ProxyLatency.js | 55 | ||||
| -rw-r--r-- | src/components/Root.js | 29 | ||||
| -rw-r--r-- | src/components/SideBar.js | 8 | ||||
| -rw-r--r-- | src/components/Switch.js | 50 | ||||
| -rw-r--r-- | src/components/ToggleSwitch.js | 112 |
12 files changed, 238 insertions, 287 deletions
diff --git a/src/components/APIConfig.js b/src/components/APIConfig.js index 9654bce..5746040 100644 --- a/src/components/APIConfig.js +++ b/src/components/APIConfig.js @@ -27,12 +27,9 @@ function APIConfig2() { const handleInputOnChange = e => { const target = e.target; const { name } = target; - let value = target.value.trim(); - - if (value === '') return; + let value = target.value; switch (name) { case 'port': - if (Number(value) < 0 || Number(value) > 65535) return; setPort(value); break; case 'hostname': diff --git a/src/components/Button.js b/src/components/Button.js index 34fe5fe..eb50ffd 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -1,25 +1,20 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import s0 from 'c/Button.module.scss'; +const noop = () => {}; -class Button extends Component { - static propTypes = { - label: PropTypes.string.isRequired, - onClick: PropTypes.func - }; +const Button = React.memo(function Button({ label, onClick = noop }) { + return ( + <button className={s0.btn} onClick={onClick}> + {label} + </button> + ); +}); - static defaultProps = { - onClick: () => {} - }; - - render() { - return ( - <button className={s0.btn} onClick={this.props.onClick}> - {this.props.label} - </button> - ); - } -} +Button.propTypes = { + label: PropTypes.string.isRequired, + onClick: PropTypes.func +}; export default Button; diff --git a/src/components/Config.js b/src/components/Config.js index ca6f9e4..e8fbfba 100644 --- a/src/components/Config.js +++ b/src/components/Config.js @@ -1,4 +1,5 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; import { useComponentState, useActions } from 'm/store'; import { getConfigs, fetchConfigs, updateConfigs } from 'd/configs'; @@ -50,30 +51,76 @@ const actions = { const mapStateToProps = s => ({ configs: getConfigs(s) }); -export default function Config2() { - const { fetchConfigs, updateConfigs } = useActions(actions); +export default function ConfigContainer() { + const { fetchConfigs } = useActions(actions); const { configs } = useComponentState(mapStateToProps); useEffect(() => { fetchConfigs(); }, []); + return <Config configs={configs} />; +} - function handleInputOnChange(ev) { - const target = ev.target; - const { name } = target; +function Config({ configs }) { + const { updateConfigs } = useActions(actions); + // configState to track component internal state + // prevConfigs to track external props.configs + const [configState, _setConfigState] = useState(configs); + const [prevConfigs, setPrevConfigs] = useState(configs); + // equivalent of getDerivedStateFromProps + if (prevConfigs !== configs) { + setPrevConfigs(configs); + _setConfigState(configs); + } - let value; - switch (target.type) { - case 'checkbox': + const setConfigState = (name, val) => { + _setConfigState({ + ...configState, + [name]: val + }); + }; + + function handleInputOnChange(e) { + const target = e.target; + const { name } = target; + let { value } = target; + switch (target.name) { + case 'allow-lan': value = target.checked; + setConfigState(name, value); + updateConfigs({ [name]: value }); + break; + case 'mode': + case 'log-level': + setConfigState(name, value); + updateConfigs({ [name]: value }); break; - case 'number': - value = Number(target.value); + case 'redir-port': + case 'socket-port': + case 'port': + if (target.value !== '') { + const num = parseInt(target.value, 10); + if (num < 0 || num > 65535) return; + } + setConfigState(name, value); break; default: - value = target.value; + return; + } + } + + function handleInputOnBlur(e) { + const target = e.target; + const { name, value } = target; + switch (name) { + case 'port': + case 'socket-port': + case 'redir-port': { + const num = parseInt(value, 10); + if (num < 0 || num > 65535) return; + updateConfigs({ [name]: num }); + break; + } } - if (configs[name] === value) return; - updateConfigs({ [name]: value }); } return ( @@ -84,8 +131,9 @@ export default function Config2() { <div className={s0.label}>HTTP Proxy Port</div> <Input name="port" - value={configs.port} + value={configState.port} onChange={handleInputOnChange} + onBlur={handleInputOnBlur} /> </div> @@ -93,8 +141,9 @@ export default function Config2() { <div className={s0.label}>SOCKS5 Proxy Port</div> <Input name="socket-port" - value={configs['socket-port']} + value={configState['socket-port']} onChange={handleInputOnChange} + onBlur={handleInputOnBlur} /> </div> @@ -102,8 +151,9 @@ export default function Config2() { <div className={s0.label}>Redir Port</div> <Input name="redir-port" - value={configs['redir-port']} + value={configState['redir-port']} onChange={handleInputOnChange} + onBlur={handleInputOnBlur} /> </div> @@ -111,7 +161,7 @@ export default function Config2() { <div className={s0.label}>Allow LAN</div> <Switch name="allow-lan" - checked={configs['allow-lan']} + checked={configState['allow-lan']} onChange={handleInputOnChange} /> </div> @@ -121,7 +171,7 @@ export default function Config2() { <ToggleSwitch options={optionsRule} name="mode" - value={configs.mode} + value={configState.mode} onChange={handleInputOnChange} /> </div> @@ -131,7 +181,7 @@ export default function Config2() { <ToggleSwitch options={optionsLogLevel} name="log-level" - value={configs['log-level']} + value={configState['log-level']} onChange={handleInputOnChange} /> </div> @@ -139,3 +189,7 @@ export default function Config2() { </div> ); } + +Config.propTypes = { + configs: PropTypes.object +}; diff --git a/src/components/ContentHeader.js b/src/components/ContentHeader.js index 3e1370b..24d887b 100644 --- a/src/components/ContentHeader.js +++ b/src/components/ContentHeader.js @@ -1,20 +1,18 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import s0 from './ContentHeader.module.scss'; -class ContentHeader extends Component { - static propTypes = { - title: PropTypes.string.isRequired - }; - - render() { - return ( - <div className={s0.root}> - <h1 className={s0.h1}>{this.props.title}</h1> - </div> - ); - } +function ContentHeader({ title }) { + return ( + <div className={s0.root}> + <h1 className={s0.h1}>{title}</h1> + </div> + ); } -export default ContentHeader; +ContentHeader.propTypes = { + title: PropTypes.string.isRequired +}; + +export default React.memo(ContentHeader); diff --git a/src/components/Icon.js b/src/components/Icon.js index a9a6393..806f455 100644 --- a/src/components/Icon.js +++ b/src/components/Icon.js @@ -19,4 +19,4 @@ Icon.propTypes = { className: PropTypes.string }; -export default Icon; +export default React.memo(Icon); diff --git a/src/components/Input.js b/src/components/Input.js index 1d66214..f621b43 100644 --- a/src/components/Input.js +++ b/src/components/Input.js @@ -1,61 +1,16 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import s0 from './Input.module.scss'; -class Input extends Component { - static propTypes = { - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - type: PropTypes.string, - onChange: PropTypes.func, - name: PropTypes.string, - placeholder: PropTypes.string - }; - - static defaultProps = { - type: 'number', - placeholder: 'Please input' - }; - - state = { - value: this.props.value, - lastValueFromProps: this.props.value - }; - - static getDerivedStateFromProps(props, state) { - if (props.value !== state.lastValueFromProps) { - return { - lastValueFromProps: props.value, - value: props.value - }; - } - return null; - } - - handleInputOnChange = e => { - const value = e.target.value; - const int = parseInt(value, 10); - if (int < 0 || int > 65535) return; - this.setState({ value: e.target.value }); - }; - - render() { - const { onChange, name, type, placeholder } = this.props; - const { value } = this.state; - return ( - <div> - <input - className={s0.input} - name={name} - type={type} - placeholder={placeholder} - value={value} - onBlur={onChange} - onChange={this.handleInputOnChange} - /> - </div> - ); - } +export default function Input(props) { + return <input className={s0.input} {...props} />; } -export default Input; +Input.propTypes = { + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + type: PropTypes.string, + onChange: PropTypes.func, + name: PropTypes.string, + placeholder: PropTypes.string +}; diff --git a/src/components/Modal.js b/src/components/Modal.js index 2bb6c09..19accdc 100644 --- a/src/components/Modal.js +++ b/src/components/Modal.js @@ -1,34 +1,28 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import Modal from 'react-modal'; import s0 from './Modal.module.scss'; -class ModalAPIConfig extends Component { - static propTypes = { - isOpen: PropTypes.bool.isRequired, - onRequestClose: PropTypes.func.isRequired - }; - - handleClick = e => { - e.preventDefault(); - }; - - render() { - const { isOpen, onRequestClose, children, ...rest } = this.props; - return ( - <Modal - isOpen={isOpen} - onRequestClose={onRequestClose} - contentLabel="test" - className={s0.content} - overlayClassName={s0.overlay} - {...rest} - > - {children} - </Modal> - ); - } +function ModalAPIConfig({ isOpen, onRequestClose, children, ...otherProps }) { + return ( + <Modal + isOpen={isOpen} + onRequestClose={onRequestClose} + contentLabel="API-Config" + className={s0.content} + overlayClassName={s0.overlay} + {...otherProps} + > + {children} + </Modal> + ); } -export default ModalAPIConfig; +ModalAPIConfig.propTypes = { + isOpen: PropTypes.bool.isRequired, + onRequestClose: PropTypes.func.isRequired, + children: PropTypes.node.isRequired +}; + +export default React.memo(ModalAPIConfig); diff --git a/src/components/ProxyLatency.js b/src/components/ProxyLatency.js index 5d1aaee..96dd26f 100644 --- a/src/components/ProxyLatency.js +++ b/src/components/ProxyLatency.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useMemo } from 'react'; import PropTypes from 'prop-types'; import s0 from './ProxyLatency.module.scss'; @@ -10,35 +10,30 @@ const colorMap = { na: '#909399' }; -class C extends Component { - static propTypes = { - latency: PropTypes.shape({ - number: PropTypes.number, - error: PropTypes.string - }) - }; - - render() { - const { latency } = this.props; - const { number, error } = latency; - let bg; - - if (error !== '') { - bg = colorMap.na; - } else if (number < 200) { - bg = colorMap.good; - } else if (number < 400) { - bg = colorMap.normal; - } else { - bg = colorMap.bad; - } - - return ( - <span className={s0.proxyLatency} style={{ color: bg }}> - {error !== '' ? <span>{error}</span> : <span>{number} ms</span>} - </span> - ); +function getLabelColor(number, error) { + if (error !== '') { + return colorMap.na; + } else if (number < 200) { + return colorMap.good; + } else if (number < 400) { + return colorMap.normal; } + return colorMap.bad; } -export default C; +export default function ProxyLatency({ latency }) { + const { number, error } = latency; + const color = useMemo(() => getLabelColor(number, error), [number, error]); + return ( + <span className={s0.proxyLatency} style={{ color }}> + {error !== '' ? <span>{error}</span> : <span>{number} ms</span>} + </span> + ); +} + +ProxyLatency.propTypes = { + latency: PropTypes.shape({ + number: PropTypes.number, + error: PropTypes.string + }) +}; diff --git a/src/components/Root.js b/src/components/Root.js index 96ae243..e9d9141 100644 --- a/src/components/Root.js +++ b/src/components/Root.js @@ -1,6 +1,5 @@ import React from 'react'; -import { Provider } from 'react-redux'; -import { Provider as StoreProvider } from 'm/store'; +import { Provider } from 'm/store'; import { HashRouter as Router, Route } from 'react-router-dom'; // import { hot } from 'react-hot-loader'; // import createHistory from 'history/createHashHistory'; @@ -26,21 +25,19 @@ window.store = store; const Root = () => ( <Provider store={store}> - <StoreProvider store={store}> - <Router> - <div className={s0.app}> - <APIDiscovery /> - <Route path="/" component={SideBar} /> - <div className={s0.content}> - <Route exact path="/" render={() => <Home />} /> - <Route exact path="/overview" render={() => <Home />} /> - <Route exact path="/configs" render={() => <Config />} /> - <Route exact path="/logs" render={() => <Logs />} /> - <Route exact path="/proxies" render={() => <Proxies />} /> - </div> + <Router> + <div className={s0.app}> + <APIDiscovery /> + <Route path="/" render={() => <SideBar />} /> + <div className={s0.content}> + <Route exact path="/" render={() => <Home />} /> + <Route exact path="/overview" render={() => <Home />} /> + <Route exact path="/configs" render={() => <Config />} /> + <Route exact path="/logs" render={() => <Logs />} /> + <Route exact path="/proxies" render={() => <Proxies />} /> </div> - </Router> - </StoreProvider> + </div> + </Router> </Provider> ); // <Route exact path="/__0" component={StyleGuide} /> diff --git a/src/components/SideBar.js b/src/components/SideBar.js index b8cd61f..cee0960 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -12,14 +12,14 @@ import yacd from 's/yacd.svg'; import s from 'c/SideBar.module.scss'; -function SideBarRow({ iconId, labelText, to }) { +const SideBarRow = React.memo(function SideBarRow({ iconId, labelText, to }) { return ( <NavLink exact to={to} className={s.row} activeClassName={s.rowActive}> <Icon id={iconId} width={28} height={28} /> <div className={s.label}>{labelText}</div> </NavLink> ); -} +}); SideBarRow.propTypes = { to: PropTypes.string.isRequired, @@ -27,7 +27,7 @@ SideBarRow.propTypes = { labelText: PropTypes.string }; -export default function SideBar() { +function SideBar() { return ( <div className={s.root}> <div className={s.logo}> @@ -43,3 +43,5 @@ export default function SideBar() { </div> ); } + +export default React.memo(SideBar); diff --git a/src/components/Switch.js b/src/components/Switch.js index 5a2119a..f956b6d 100644 --- a/src/components/Switch.js +++ b/src/components/Switch.js @@ -1,35 +1,27 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import s0 from 'c/Switch.module.scss'; +const noop = () => {}; -class Switch extends Component { - static propTypes = { - checked: PropTypes.bool, - onChange: PropTypes.func, - name: PropTypes.string - }; - - static defaultProps = { - checked: false, - name: '', - onChange: () => {} - }; - - render() { - const { checked, onChange, name } = this.props; - return ( - <div> - <input - type="checkbox" - name={name} - checked={checked} - className={s0.switch} - onChange={onChange} - /> - </div> - ); - } +function Switch({ checked = false, onChange = noop, name = '' }) { + return ( + <div> + <input + type="checkbox" + name={name} + checked={checked} + className={s0.switch} + onChange={onChange} + /> + </div> + ); } -export default Switch; +Switch.propTypes = { + checked: PropTypes.bool, + onChange: PropTypes.func, + name: PropTypes.string +}; + +export default React.memo(Switch); diff --git a/src/components/ToggleSwitch.js b/src/components/ToggleSwitch.js index cdd4726..7303984 100644 --- a/src/components/ToggleSwitch.js +++ b/src/components/ToggleSwitch.js @@ -1,77 +1,49 @@ -import React, { Component } from 'react'; +import React, { useRef } from 'react'; import PropTypes from 'prop-types'; import s0 from 'c/ToggleSwitch.module.scss'; -class ToggleSwitch extends Component { - static propTypes = { - options: PropTypes.array, - value: PropTypes.string, - name: PropTypes.string, - onChange: PropTypes.func - }; - - static defaultProps = { - options: [ - { - label: 'Global', - value: 'Global' - }, - { - label: 'Rule', - value: 'Rule' - }, - { - label: 'Direct', - value: 'Direct' - } - ], - value: 'Rule', - name: 'rand0' - }; - - // handleRadioOnChange = ev => { - // ev.preventDefault(); - // const value = ev.target.value; - // if (this.state.value === value) return; - // this.setState({ value }); - // }; - - render() { - const { options, name, value, onChange } = this.props; - const w = (100 / options.length).toPrecision(3); - return ( - <div> - <div className={s0.ToggleSwitch}> - {options.map((o, idx) => { - if (value === o.value) this.idx = idx; - const id = `${name}-${o.label}`; - let className = idx === 0 ? '' : 'border-left'; - return ( - <label htmlFor={id} key={id} className={className}> - <input - id={id} - name={name} - type="radio" - value={o.value} - checked={value === o.value} - onChange={onChange} - /> - <div>{o.label}</div> - </label> - ); - })} - <a - className={s0.slider} - style={{ - width: w + '%', - left: this.idx * w + '%' - }} - /> - </div> +function ToggleSwitch2({ options, value, name, onChange }) { + const idxRef = useRef(null); + const w = (100 / options.length).toPrecision(3); + return ( + <div> + <div className={s0.ToggleSwitch}> + {options.map((o, idx) => { + if (value === o.value) idxRef.current = idx; + const id = `${name}-${o.label}`; + let className = idx === 0 ? '' : 'border-left'; + return ( + <label htmlFor={id} key={id} className={className}> + <input + id={id} + name={name} + type="radio" + value={o.value} + checked={value === o.value} + onChange={onChange} + /> + <div>{o.label}</div> + </label> + ); + })} + <a + className={s0.slider} + style={{ + width: w + '%', + left: idxRef.current * w + '%' + }} + /> </div> - ); - } + </div> + ); } -export default ToggleSwitch; +ToggleSwitch2.propTypes = { + options: PropTypes.array, + value: PropTypes.string, + name: PropTypes.string, + onChange: PropTypes.func +}; + +export default React.memo(ToggleSwitch2); |
