反应导航和反应上下文

Posted

技术标签:

【中文标题】反应导航和反应上下文【英文标题】:React Navigation and React Context 【发布时间】:2021-08-01 02:31:22 【问题描述】:

在我们的应用程序中,我们为每个标签使用标签导航和堆栈导航。我们想要一组设备,我们可以在其中添加和删除设备。该数组应该在每个选项卡上都可用。

这是我们的供应商

import React from 'react'
const DevicesContext = React.createContext('')
export default DevicesContext

这是我们的 app.js

import React, useState from 'react';
import uuid from 'react-native-uuid';
import  NavigationContainer  from '@react-navigation/native';
import  createMaterialBottomTabNavigator  from '@react-navigation/material-bottom-tabs';
import  MaterialCommunityIcons  from '@expo/vector-icons';
import  Feather  from '@expo/vector-icons';
import  MaterialIcons  from '@expo/vector-icons';

import HomeStackScreen from "./components/home/HomeStackScreen";
import ConnectStackScreen from "./components/connect/ConnectStackScreen";
import SettingsStackScreen from "./components/settings/SettingsStackScreen";

import DevicesContext from "./components/context/DevicesContext";

const Tab = createMaterialBottomTabNavigator();

const deleteItem = (id) => 
    setDevices(prevDevice => 
        return prevDevice.filter(device => device.id != id)
    )
    console.log(devices)


const addItem = (device) => 
    setDevices(prevDevices => 
        return [id: uuid.v4(), name:device, ...prevDevices];
    )

function MyTabs() 
    return (
        <Tab.Navigator
            initialRouteName="Home"
            activeColor="#E4E4E4"
            inactiveColor="#000000"
            shifting=true
            labelStyle= fontSize: 12 
            barStyle= backgroundColor: '#8DFFBB' 
        >

            <Tab.Screen
                name="Devices"
                component=ConnectStackScreen
                options=
                    tabBarLabel: 'Geräte',
                    tabBarIcon: ( color ) => (
                        <MaterialIcons name="devices" size=24 color=color />
                    ),
                
            />
            <Tab.Screen
                name="Home"
                component=HomeStackScreen
                options=
                    tabBarLabel: 'Home',
                    tabBarIcon: ( color ) => (
                        <MaterialCommunityIcons name="home" color=color size=26 />
                    ),
                
            />
            <Tab.Screen
                name="Settings"
                component=SettingsStackScreen
                options=
                    tabBarLabel: 'Einstellungen',
                    tabBarIcon: ( color ) => (
                        <Feather name="settings" size=24 color=color />
                    ),
                
            />
        </Tab.Navigator>
    );


export default function App() 
    const [devices, setDevices] = useState([
        id: uuid.v4(), name: 'thing 1', ip: 5,
        id: uuid.v4(), name: 'thing 2', ip: 2,
        id: uuid.v4(), name: 'thing 3', ip: 6,
        id: uuid.v4(), name: 'thing 4', ip: 10,
    ])
    return (
        <DevicesContext.Provider value=devices>
        <NavigationContainer>

                <MyTabs />

        </NavigationContainer>
        </DevicesContext.Provider>
    );

这是我们可以添加设备的连接屏幕

import React, useContext, useState from 'react';
import Text, View, Button, FlatList, StyleSheet, TouchableOpacity, Image from 'react-native';
import uuid from 'react-native-uuid';
import ListItem from "../shared/ListItem";
import AddItem from "../shared/AddItem";
import DevicesContext from "../context/DevicesContext";

function ConnectScreen( navigation) 
    const [devices, setDevices] = useState(useContext(DevicesContext));

    const deleteItem = (id) => 
        setDevices(prevDevice => 
            return prevDevice.filter(device => device.id != id)
        )
        console.log(devices)
    

    const addItem = (device) => 
        setDevices(prevDevices => 
            return [id: uuid.v4(), name:device, ...prevDevices];
        )
    
    return (
        <View style=padding: 10, flex: 1, justifyContent: 'center'>
            <View style=styles.AddNormal>
                <AddItem addItem=addItem></AddItem>
                <FlatList style=styles.List data=devices renderItem=(item) => (
                    <ListItem item=item deleteItem=deleteItem></ListItem>
                )/>
            </View>
            <View style=styles.AddQr>
                <Image source=require('../../img/qr-code-url.png')  style= width: 150, height: 150, marginBottom: 10  />
                <Text style= textAlign: 'center', marginBottom: 10 >Du kannst außerdem ein Gerät durch das scannen eines Qr-Code hinzufügen</Text>
                <TouchableOpacity onPress=() => navigation.navigate('QrCode')style=styles.btn>
                <Text style=styles.btnText>Qr-Code scannen</Text>
            </TouchableOpacity>

            </View>
        </View>
    );

const styles = StyleSheet.create(
    List: 
        backgroundColor: '#E4E4E4',
    ,
    AddNormal: 
        padding: 10, flex: 1,
    ,
    AddQr: 
        backgroundColor: '#E4E4E4',
        padding: 30,
        flex: 1,
        marginTop: 20,
        marginBottom: 20,
        alignItems: 'center'
    ,
    btn: 
        backgroundColor: '#8DFFBB',
        padding: 9,
        margin: 10,
    ,
    btnText: 
        color: '#000',
        fontSize: 20,
        textAlign: 'center',
    
);

export default ConnectScreen;

这是我们的主屏幕

import React, useState, useContext, useEffect from 'react';
import Button, FlatList, SafeAreaView, StatusBar, StyleSheet, Text, TouchableOpacity, View from "react-native";
import ServerOnOffSwitch, SendMessage from "./network";
import DevicesContext from "../context/DevicesContext";


const Item = ( item, onPress, backgroundColor, textColor ) => (
    <TouchableOpacity onPress=onPress style=[styles.item, backgroundColor]>
        <Text style=[styles.title, textColor]>item.name</Text>
    </TouchableOpacity>
);

function HomeScreen ()
    const [devices, setDevices] = useState(useContext(DevicesContext));

    const deleteItem = (id) => 
        setDevices(prevDevice => 
            return prevDevice.filter(device => device.id != id)
        )
    
    const [selectedId, setSelectedId] = useState(null);

    const renderItem = ( item ) => 
        const backgroundColor = item.id === selectedId ? "#b5b5b5" : "#ededed";
        const color = item.id === selectedId ? 'white' : 'black';

        return (
            <Item
                item=item
                onPress=() => setSelectedId(item.id)
                backgroundColor= backgroundColor 
                textColor= color 
            />
        );
    ;

    return (
        <View style=padding: 10, flex: 1, justifyContent: 'center'>

            <View style=padding: 10, flex: 1>
                <Text style=styles.DeviceHeader>Gerät auswählen</Text>
                <FlatList
                    data=devices
                    renderItem=renderItem
                    keyExtractor=(item) => item.id
                    extraData=selectedId
                />
            </View>

        <View style=padding: 10, flex: 1, justifyContent: 'center', alignItems: 'center'>
            <SendMessage item=selectedId></SendMessage>
            <ServerOnOffSwitch></ServerOnOffSwitch>
        </View>
        </View>
    );


const styles = StyleSheet.create(
    DeviceHeader: 
        fontSize: 22,
        paddingBottom: 10,
    ,
    item: 
        padding: 10,
        backgroundColor: '#f8f8f8',
        borderBottomWidth: 1,
        borderColor: '#eee',
    ,
    title: 
        fontSize: 18,
    ,
);

export default HomeScreen;

如果我们在连接屏幕中添加设备,它们会在那里更新,但不会在主屏幕上。 感谢您的帮助:)

【问题讨论】:

【参考方案1】:

要从嵌套组件更新上下文,您必须传递 setDevices 方法,它将更新它。 要通过它,请执行以下步骤:

你的上下文应该是

import React from 'react'
const DevicesContext = React.createContext(
    devices: [],
    setDevices: () => , //methode will update context value
    
)
export default DevicesContext

App.js 应该是


//define state
const [devices, setDevices] = React.useState([])
//define constexValue
//we will pass `devices` and also `setDevices` that will update it.
const DevicesContextValue = React.useMemo(() => ( devices, setDevices), [devices]);

return (
  <DevicesContext.Provider value=DevicesContextValue>
       ...
  </DevicesContext.Provider>
);

ConnectScreen.js 应该是

function ConnectScreen()
    const devices, setDevices = useContext(DevicesContext);
    //call setDevices will update context
    ....

HomeScreen.js 应该是

function HomeScreen ()
    const devices, setDevices = useContext(DevicesContext);
    //use devices from context in your flatlist and when the context update the result will show in flatlist
    ....


【讨论】:

非常感谢它为我们所用。我们唯一需要改变的是“const [devices, setDevices]”到“const devices, setDevices”

以上是关于反应导航和反应上下文的主要内容,如果未能解决你的问题,请参考以下文章

未捕获的错误:在路由器上下文之外呈现的 <Link> 无法导航。(…)反应

使用上下文和钩子更新未安装组件的状态 - 反应原生

更新的反应上下文值未反映在上一个屏幕中

在重定向之前需要反应上下文来更新

如何在本机反应中模拟上下文消费者反应元素

反应原生导航在屏幕之间移动