Skip to content

一,组件使用

自定义组件

一般通用功能,封装成一个独立的组件,方便复用 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';