Skip to content

Commit eff5f36

Browse files
authored
Merge pull request #500 from nullptr-z/dev
refactor(Switch): 使用函数组件替换Class组件
2 parents b2bf550 + a707aad commit eff5f36

File tree

1 file changed

+159
-156
lines changed

1 file changed

+159
-156
lines changed

packages/core/src/Switch/index.tsx

Lines changed: 159 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React from 'react';
1+
import { number } from 'prop-types';
2+
import React, { useState, useEffect, useMemo } from 'react';
23
import {
34
TouchableOpacity,
45
Animated,
@@ -33,173 +34,175 @@ export interface SwitchState {
3334
animatedStart: (checked: boolean) => void;
3435
}
3536

36-
export default class Switch extends React.Component<SwitchProps, SwitchState> {
37-
translateXValue: number = 2;
38-
height: number = 16;
39-
static defaultProps: SwitchProps = {
40-
checked: false,
41-
size: 'default',
42-
thumbColor: '#fff',
43-
color: '#4DD964',
44-
onValueChange: () => {},
45-
};
46-
private animatedStart: (checked: boolean) => void;
47-
constructor(props: SwitchProps) {
48-
super(props);
49-
this.animatedStart = (checked: boolean) => {
50-
const obj = {
51-
height: this.height,
52-
number: 1,
53-
translateXValue: this.translateXValue,
54-
};
55-
if (!checked) {
56-
obj.height = 2;
57-
obj.number = 0;
58-
obj.translateXValue = 2;
59-
}
60-
Animated.parallel([
61-
Animated.sequence([
62-
Animated.spring(this.state.borderValue, {
63-
toValue: obj.height,
64-
overshootClamping: true,
65-
useNativeDriver: false,
66-
}),
67-
Animated.spring(this.state.bgOpacity, {
68-
toValue: obj.number,
69-
overshootClamping: true,
70-
useNativeDriver: false,
71-
}),
72-
]),
73-
Animated.spring(this.state.translateXValue, {
74-
toValue: obj.translateXValue,
37+
function Switch(props: SwitchProps) {
38+
const {
39+
style,
40+
size,
41+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
42+
checked = false,
43+
color,
44+
disabled,
45+
thumbColor,
46+
trackStyle,
47+
thumbStyle,
48+
...otherProps
49+
} = props;
50+
const [height, setHeight] = useState(16);
51+
const [translateXValue, setTranslateXValue] = useState(2);
52+
53+
const animatedStart = (checked: boolean) => {
54+
const obj = {
55+
height: height,
56+
number: 1,
57+
translateXValue: translateXValue,
58+
};
59+
60+
if (!checked) {
61+
obj.height = 2;
62+
obj.number = 0;
63+
obj.translateXValue = 2;
64+
}
65+
66+
Animated.parallel([
67+
Animated.sequence([
68+
Animated.spring(state.borderValue, {
69+
toValue: obj.height,
7570
overshootClamping: true,
7671
useNativeDriver: false,
7772
}),
78-
]).start();
79-
};
80-
this.state = {
81-
checked: !!this.props.checked,
82-
containerSize: { width: 0, height: 0 },
83-
borderValue: new Animated.Value(0),
84-
translateXValue: new Animated.Value(2),
85-
bgOpacity: new Animated.Value(props.value ? 1 : 0),
86-
control: 'state',
87-
animatedStart: this.animatedStart,
88-
};
89-
this.animatedStart(!!this.props.checked);
90-
}
91-
static getDerivedStateFromProps(props: SwitchProps, state: SwitchState) {
92-
if (state.control === 'state') {
93-
return {
94-
control: 'props',
95-
};
96-
}
97-
if (props.checked !== state.checked) {
98-
state.animatedStart(!!props.checked);
99-
return {
100-
checked: !!props.checked,
101-
control: 'props',
102-
};
103-
}
104-
return null;
105-
}
106-
onPress = () => {
107-
const checked = !this.state.checked;
108-
this.setState({ checked, control: 'state' }, () => {
109-
this.animatedStart(checked);
110-
this.props.onValueChange!(checked);
111-
});
73+
Animated.spring(state.bgOpacity, {
74+
toValue: obj.number,
75+
overshootClamping: true,
76+
useNativeDriver: false,
77+
}),
78+
]),
79+
Animated.spring(state.translateXValue, {
80+
toValue: obj.translateXValue,
81+
overshootClamping: true,
82+
useNativeDriver: false,
83+
}),
84+
]).start();
11285
};
113-
measureContainer = (event: LayoutChangeEvent) => {
114-
const { checked } = this.state;
115-
const { translateXValue } = this.state;
86+
87+
const [state, setState] = useState<SwitchState>({
88+
checked: checked,
89+
containerSize: { width: 0, height: 0 },
90+
borderValue: new Animated.Value(0),
91+
translateXValue: new Animated.Value(2),
92+
bgOpacity: new Animated.Value(props.value ? 1 : 0),
93+
control: 'state',
94+
animatedStart: animatedStart,
95+
});
96+
97+
const { containerSize } = state;
98+
99+
const onPress = () => {
100+
const checked = !state.checked;
101+
setState({ ...state, checked, control: 'state' });
102+
103+
animatedStart(checked);
104+
props.onValueChange!(checked);
105+
};
106+
107+
const measureContainer = (event: LayoutChangeEvent) => {
108+
animatedStart(!!props.checked);
109+
let { checked, translateXValue } = state;
110+
const {} = state;
116111
const { height: layoutHeight, width: layoutWidth } = event.nativeEvent.layout;
117-
const height = layoutHeight - 4;
112+
let height = layoutHeight - 4;
118113
const width = height;
119114
const size = { width, height };
120-
this.height = height / 2;
121-
const state = { containerSize: size };
122-
this.translateXValue = layoutWidth - 2 - width;
115+
setHeight(height / 2);
116+
setTranslateXValue(layoutWidth - 2 - width);
123117
translateXValue.setValue(checked ? layoutWidth - 2 - width : 2);
124-
this.setState({ ...state, control: 'state' }, () => {
125-
this.animatedStart(!!this.props.checked);
126-
});
118+
setState({ ...state, containerSize: size, control: 'state' });
119+
animatedStart(!!props.checked);
127120
};
128-
render() {
129-
const {
130-
style,
131-
size,
132-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
133-
checked,
134-
color,
135-
disabled,
136-
thumbColor,
137-
trackStyle,
138-
thumbStyle,
139-
...otherProps
140-
} = this.props;
141-
const { containerSize } = this.state;
142-
const bgBorder = this.state.borderValue.interpolate({
143-
inputRange: [2, this.height],
144-
outputRange: [2, this.height],
145-
// extrapolate: 'clamp',
146-
});
147-
const sizeStyl: ViewStyle = {};
148-
switch (size) {
149-
case 'small':
150-
sizeStyl.height = 20;
151-
sizeStyl.width = 30;
152-
break;
153-
case 'large':
154-
sizeStyl.height = 30;
155-
sizeStyl.width = 48;
156-
break;
157-
default:
158-
sizeStyl.height = 26;
159-
sizeStyl.width = 38;
160-
break;
161-
}
162-
return (
163-
<View {...otherProps} onLayout={this.measureContainer} style={[styles.warpper, sizeStyl, style]}>
164-
{disabled && <View style={[styles.position, styles.disabled]} />}
165-
<Animated.View style={[styles.bg, styles.position, trackStyle, { borderWidth: bgBorder }]} />
166-
<TouchableOpacity
167-
// eslint-disable-next-line
168-
style={[styles.position, { zIndex: 1 }]}
169-
onPress={this.onPress}
170-
/>
171-
<Animated.View
172-
style={[
173-
styles.position,
174-
// eslint-disable-next-line
175-
{
176-
backgroundColor: this.state.checked ? color : '',
177-
borderRadius: 16,
178-
opacity: this.state.bgOpacity,
179-
},
180-
]}
181-
/>
182-
<Animated.View
183-
style={[
184-
styles.grip,
185-
186-
disabled ? styles.shadowDisable : styles.shadow,
187-
{
188-
backgroundColor: thumbColor,
189-
width: containerSize.width,
190-
height: containerSize.height,
191-
},
192-
thumbStyle,
193-
{
194-
transform: [{ translateX: this.state.translateXValue }],
195-
},
196-
]}
197-
/>
198-
</View>
199-
);
121+
122+
// if (state.control === 'state') {
123+
// setState({ ...state, control: 'props' });
124+
// }
125+
// if (props.checked !== state.checked) {
126+
// state.animatedStart(!!props.checked);
127+
// setState({
128+
// ...state,
129+
// checked: !!props.checked,
130+
// control: 'props',
131+
// });
132+
// }
133+
134+
const bgBorder = state.borderValue.interpolate({
135+
inputRange: [2, height],
136+
outputRange: [2, height],
137+
// extrapolate: 'clamp',
138+
});
139+
140+
const sizeStyl: ViewStyle = {};
141+
switch (size) {
142+
case 'small':
143+
sizeStyl.height = 20;
144+
sizeStyl.width = 30;
145+
break;
146+
case 'large':
147+
sizeStyl.height = 30;
148+
sizeStyl.width = 48;
149+
break;
150+
default:
151+
sizeStyl.height = 26;
152+
sizeStyl.width = 38;
153+
break;
200154
}
155+
156+
return (
157+
<View {...otherProps} onLayout={measureContainer} style={[styles.warpper, sizeStyl, style]}>
158+
{disabled && <View style={[styles.position, styles.disabled]} />}
159+
<Animated.View style={[styles.bg, styles.position, trackStyle, { borderWidth: bgBorder }]} />
160+
<TouchableOpacity
161+
// eslint-disable-next-line
162+
style={[styles.position, { zIndex: 1 }]}
163+
onPress={onPress}
164+
/>
165+
<Animated.View
166+
style={[
167+
styles.position,
168+
// eslint-disable-next-line
169+
{
170+
backgroundColor: state.checked ? color : '',
171+
borderRadius: 16,
172+
opacity: state.bgOpacity,
173+
},
174+
]}
175+
/>
176+
<Animated.View
177+
style={[
178+
styles.grip,
179+
180+
disabled ? styles.shadowDisable : styles.shadow,
181+
{
182+
backgroundColor: thumbColor,
183+
width: containerSize.width,
184+
height: containerSize.height,
185+
},
186+
thumbStyle,
187+
{
188+
transform: [{ translateX: state.translateXValue }],
189+
},
190+
]}
191+
/>
192+
</View>
193+
);
201194
}
202195

196+
Switch.defaultProps = {
197+
checked: false,
198+
size: 'default',
199+
thumbColor: '#fff',
200+
color: '#4DD964',
201+
onValueChange: () => {},
202+
};
203+
204+
export default Switch;
205+
203206
const styles = StyleSheet.create({
204207
warpper: {
205208
position: 'relative',

0 commit comments

Comments
 (0)