Appearance
一,组件使用
自定义组件
一般通用功能,封装成一个独立的组件,方便复用 1,父组件给子组件传递信息,
2,子组件的操作反馈给父组件
3,父组件直接操作子组件
比如海报,弹框,列表,键盘等,小到一个标题,大到一个页面都可以是复用的组件; 或者在应用开发过程中实际需要的功能;
定义
javascript
const CommonBtn = (props) => {
const {
name = "我是按钮",
defaultFocus = false,
callBack,
config = {},
} = props;
const [count, setCount] = useState(0);
const [bgColor, setBgColor] = useState("rgba(255,255,255,0.3)");
function onPress() {
console.log("[CommonBtn] onPress", name);
setCount(count + 1);
callBack && callBack(); //子组件将结果返回给父组件
}
function onFocus() {
console.log("[CommonBtn] onfocus", name);
setBgColor("#FFFFFF");
}
function onBlur() {
console.log("[CommonBtn] onBlue", name);
setBgColor("rgba(255,255,255,0.3)");
}
return (
<TouchableHighlight
onPress={onPress}
onFocus={onFocus}
onBlur={onBlur}
hasTVPreferredFocus={defaultFocus}
activeOpacity={0.3}
underlayColor="rgba(255,255,255,0.3)"
style={[
{
width: 100,
height: 50,
justifyContent: "center",
alignItems: "center",
borderColor: bgColor,
borderWidth: 2,
},
config.viewStyle,
]}
>
<View>
<Text style={{ color: "#FF4500", fontSize: 15 }}>{name}</Text>
</View>
</TouchableHighlight>
);
};
export default CommonBtn;怎么使用?
javascript
import CommonBtn from "../../components/button";
function handlerCallBack(type, params) {
console.log("[Portal][handlerCallBack]type,params", type, params);
}
//父组件向子组件传递参数
<CommonBtn
name={"详情"}
defaultFocus={true}
config={{ viewStyle: { width: 150, height: 75 } }}
callBack={(params) => {
handlerCallBack("link", params);
}}
></CommonBtn>;
<CommonBtn name={"返回"}></CommonBtn>;操作子组件
javascript
//父组件传递过来的ref
const CommonBtn = (props, ref) => {
const {
name = "我是按钮",
defaultFocus = false,
callBack,
config = {},
} = props;
const [count, setCount] = useState(0);
const [bgColor, setBgColor] = useState("rgba(255,255,255,0.3)");
// const btnRef=useRef(null)
function onPress() {
console.log("[CommonBtn] onPress", name);
setCount(count + 1);
callBack && callBack();
}
function onFocus() {
console.log("[CommonBtn] onfocus", name);
setBgColor("#FFFFFF");
}
function onBlur() {
console.log("[CommonBtn] onBlue", name);
setBgColor("rgba(255,255,255,0.3)");
}
return (
//这里的ref是父组件传递过来的ref
<TouchableHighlight
ref={ref}
onPress={onPress}
onFocus={onFocus}
onBlur={onBlur}
...
>
<View>
<Text style={{ color: "#FF4500", fontSize: 15 }}>{name}</Text>
</View>
</TouchableHighlight>
);
};
export default forwardRef(CommonBtn);javascript
//父组件中
const backRef = useRef();
useEffect(() => {
//在初始化render之后只执行一次,在这个方法内,可以访问任何组件,componentDidMount()方法中的子组件在父组件之前执行
console.log("[Portal.js]---componentDidMount");
BackHandler.addEventListener("hardwareBackPress", onBackClicked);
return async () => {
BackHandler.removeEventListener("hardwareBackPress", onBackClicked);
console.log("[Portal.js]---componentWillUnmount---");
};
}, []);
const onBackClicked = () => {
console.log("backhandle===");
//父组件拿到了子组件的整个dom对象
backRef?.current.setNativeProps({
backgroundColor: "#ff0000",
});
return true;
};
//假如按返回键,让这个按钮获得焦点,
<CommonBtn ref={backRef} name={"返回"}></CommonBtn>;json
backRef= {"current": {"_children": [[ReactNativeFiberHostComponent]], "_internalFiberInstanceHandleDEV": {"_debugHookTypes": null, "_debugID": 301, "_debugIsCurrentlyTiming": false, "_debugNeedsRemount": false, "_debugOwner": [FiberNode], "_debugSource": [Object], "actualDuration": 1, "actualStartTime": 31715, "alternate": [FiberNode], "child": [FiberNode], "childExpirationTime": 0, "dependencies": null, "effectTag": 4, "elementType": "RCTView", "expirationTime": 0, "firstEffect": [FiberNode], "index": 0, "key": null, "lastEffect": [FiberNode], "memoizedProps": [Object], "memoizedState": null, "mode": 8, "nextEffect": null, "pendingProps": [Object], "ref": [Circular], "return": [FiberNode], "selfBaseDuration": 0, "sibling": null, "stateNode": [Circular], "tag": 5, "treeBaseDuration": 1, "type": "RCTView", "updateQueue": null},
"_nativeTag": 39,
"viewConfig": {"Commands": [Object], "bubblingEventTypes": [Object], "directEventTypes": [Object], "uiViewClassName": "RCTView", "validAttributes": [Object]}
}}有的时候不需要暴露子组件全部的属性,只暴露出父组件需要的值或者方法
javascript
import React, {
useState,
forwardRef,
useRef,
useImperativeHandle,
} from "react";
import { Text, View, TouchableHighlight } from "react-native";
const CommonBtn = (props, ref) => {
const {
name = "我是按钮",
defaultFocus = false,
callBack,
config = {},
} = props;
const [count, setCount] = useState(0);
const [bgColor, setBgColor] = useState("rgba(255,255,255,0.3)");
const btnRef = useRef(null);
//这个ref就是父组件的ref,相当于给这个ref定义作为第二个参数的对象
useImperativeHandle(ref, () => ({
changeBg: () => {
console.log("[CommonBtn] btnRef", btnRef);
console.log("[CommonBtn] btnRef.btnRef", btnRef.current);
btnRef?.current?.setNativeProps({
backgroundColor: "#ff0000",
});
},
}));
return (
<TouchableHighlight
ref={btnRef}
onPress={onPress}
onFocus={onFocus}
onBlur={onBlur}
hasTVPreferredFocus={defaultFocus}
activeOpacity={0.3}
underlayColor="rgba(255,255,255,0.3)"
style={[
{
width: 100,
height: 50,
// backgroundColor: bgColor,
justifyContent: "center",
alignItems: "center",
borderColor: bgColor,
borderWidth: 2,
},
config.viewStyle,
]}
>
<View>
<Text style={{ color: "#FF4500", fontSize: 15 }}>{name}</Text>
</View>
</TouchableHighlight>
);
};
export default forwardRef(CommonBtn);父组件中
javascript
const onBackClicked = () => {
console.log("backhandle===");
console.log("[Portal.js]backRef?.current", backRef?.current);
// [Portal.js]backRef?.current {"changeBg": [Function changeBg]}
backRef?.current.changeBg();
return true;
};二,导航基本使用
导航有很多种,栈导航,抽屉导航,tab 导航等 如果 app 只用一个 stack navigator,那么效果就和浏览器操作导航类似,app 通过入栈和出栈,使得用户显示不同的页面, 怎么使用? createNativeStackNavigator()返回的对象包含 Screen and Navigator 两个属性
在 app.js 文件中里面增加如下代码,一个路由对应一个页面
javascript
import React from "react";
import { StyleSheet } from "react-native";
import Portal from "./service/portal";
import Detail from "./service/detail";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
const Stack = createStackNavigator();
const App = (props) => {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Portal"
screenOptions={{
headerShown: false,
gestureEnabled: false,
animationEnabled: false,
unmountInactiveScreen: true,
detachPreviousScreen: true,
}}
>
<Stack.Screen name="Portal" component={Portal}></Stack.Screen>
<Stack.Screen name="Detail" component={Detail}></Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;路由跳转并传递参数
const Portal = (props) => {
console.log("[Portal.js]props=",props)
function handlerCallBack(type,params){
console.log('[Portal][handlerCallBack]type,params',type,params)
if(type=='link'){
console.log("[Portal.js]props=",props.navigation.navigate)
props.navigation.navigate('Detail')
}
}
...
}props 打印
json
{"navigation": {
"addListener": [Function addListener],
"canGoBack": [Function canGoBack],
"dispatch": [Function dispatch],
"getId": [Function getId],
"getParent": [Function getParent],
"getState": [Function anonymous],
"goBack": [Function anonymous],
"isFocused": [Function isFocused],
"navigate": [Function anonymous],
"pop": [Function anonymous],
"popToTop": [Function anonymous],
"push": [Function anonymous],
"removeListener": [Function removeListener],
"replace": [Function anonymous],
"reset": [Function anonymous],
"setOptions": [Function setOptions],
"setParams": [Function anonymous]},
"route": {"key": "Portal-xqBGCDSWYcAyJnVXCpIaY", "name": "Portal", "params":undefined}}javascript
import React, { useEffect } from "react";
import { Text, View } from "react-native";
const Detail = () => {
useEffect(() => {
console.log("[Detail.js]---componentDidMount");
return async () => {
console.log("[Detail.js]---componentWillUnmount---");
};
}, []);
return (
<View>
<Text>Detail page</Text>
</View>
);
};
export default Detail;页面接收路由参数 portal 增加参数
function handlerCallBack(type,params){
console.log('[Portal][handlerCallBack]type,params',type,params)
if(type=='link'){
console.log("[Portal.js]props=",props.navigation.navigate)
props.navigation.navigate('Detail',{data:{name:'zhangsan'}})
}
}在 detail 里面增加 props 的打印
json
props= {"navigation": {"addListener": [Function addListener], "canGoBack": [Function canGoBack], "dispatch": [Function dispatch], "getId": [Function getId], "getParent": [Function getParent], "getState": [Function anonymous], "goBack": [Function anonymous], "isFocused": [Function isFocused], "navigate": [Function anonymous], "pop": [Function anonymous], "popToTop": [Function anonymous], "push": [Function anonymous], "removeListener": [Function removeListener], "replace": [Function anonymous], "reset": [Function anonymous], "setOptions": [Function setOptions], "setParams": [Function anonymous]},
"route": {"key": "Detail-fzL5FwMyAS7vrzG_Hu4he", "name": "Detail", "params": {"name": "zhangsan"}, "path": undefined}
}取出参数
javascript
import React, { useEffect } from "react";
import { Text, View } from "react-native";
const Detail = (props) => {
console.log("[Detail.js]props=", props);
const data = props.route.params;
console.log("[Detail.js]data=", data); // [Detail.js]data= {"name": "zhangsan"}
useEffect(() => {
console.log("[Detail.js]---componentDidMount");
return async () => {
console.log("[Detail.js]---componentWillUnmount---");
};
}, []);
return (
<View>
<Text>{data?.name || "游客"}</Text>
</View>
);
};
export default Detail;返回上一页,跳转到指定页
goBack()
backhandler 不做任何处理就会返回栈中上一个路由
更改栈的顺序
replace()替换当前的栈
路由跳转了组件是否销毁了?
比如说刚刚 navigate 方式,portal 组件没有销毁,跳转到 detail 页面时,detail 创建,返回到 portal 时,detail 销毁,portal 没有重新创建
监听路由
比如 detail 页面对数据进行了操作,更新了数据库,回到 portal 页面需要最新的数据,
useEffect(() => {
//在初始化render之后只执行一次,在这个方法内,可以访问任何组件,componentDidMount()方法中的子组件在父组件之前执行
console.log('[Portal.js]---componentDidMount');
BackHandler.addEventListener('hardwareBackPress', onBackClicked);
//监听路由
routerFocusSubscribe = props.navigation.addListener(
'focus',
() => {
this.routeParam = props.route?.params
console.log('[Portal]isfocus',routeParam);
//执行重新获取数据的操作
},
);
return async () => {
BackHandler.removeEventListener('hardwareBackPress', onBackClicked);
routerFocusSubscribe && routerFocusSubscribe(); //注销路由监听
console.log('[Portal.js]---componentWillUnmount---');
};
}, []);作业
第一次作业页面:
点击,“退出服务” 按钮,跳转到第二次作业的列表页面;
列表页面:
列表项抽取为单独的组件,可点击,点击某项后,页面弹框显示该项的服务编号;
进入页面 3s 后,第一条记录背景色自动更改为'#F0FFFF';