首页 > 解决方案 > react native中全局变量的动态变化

问题描述

我的 react native 应用程序中有一个 mqtt 客户端,它不断地从代理接收数据。此数据在许多不同的屏幕/组件中以不同的方式使用(我有 1 个类,每个屏幕扩展 React.Component)。例如,我在屏幕 1 上显示从主题 x 接收到的数据,同时在屏幕 2 上显示从主题 y 和主题 x 接收到的数据。

我存储这些数据的方式是使用 redux。因此,我可以使用 eg 获取最新数据this.props.dataX。我不确定这是否是存储它的最佳方式。我想要实现的是显示此数据的动态列表,例如,一个平面列表用于在第一个屏幕上的一个主题上接收到的数据,另一个平面列表用于 topic2+topic1-screen2。

如果我只是将数据发送到平面列表,它不会变成动态的。我必须刷新页面才能呈现新数据:

<FlatList
    style={styles...}
    data={this.props.dataX}
    <renderItem={({item}) => <Item item={item}/>}
    keyExtractor={item => item.name}
/>
function Item({item}) {
    return (
        <View style={styles...}>
                <Text>{item.name}</Text>
                <Image style={styles.image} source={...getDeviceIcon(item.name)}/>
        </View>
    );
}

我知道如果此数据处于组件状态,那么它将是动态的。因此,即使它没有真正意义,我也尝试在该州复制它,希望它是动态的。但它没有:

constructor(props) {
        super(props);
        this.state = {
            data: this.props.dataX
        };
}

那么我该如何实现呢?

标签: javascriptreactjsreact-native

解决方案


继续我的评论:这是我正在做的一个完整的例子。

我正在使用 mqttws31.js 库连接到蚊子。如果您需要,请告诉我。

我有加载 redux 商店的 app.js 和 App.jsx 中的应用程序。

App.jsx 处理我的 wsClient 函数,但您可以将它们移动到其他文件。

这是重新渲染我的组件的 Redux Connect 函数和 mapstateToProps。如果 props 发生变化,组件会再次渲染

商店.js:

import { createStore } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension';
import { createReducer } from '@reduxjs/toolkit'; // I'm using the toolkit here

const reducer = createReducer(initialState, {
  SET_ONE: (state, action) => {
    state.items[action.key] = action.value
  },
  SET_ALL: (state, action) => {
    state.items = action.data
  }
})

const initialState = {
  items: {}
}

const store = createStore(reducer,initialState);

export default store;

应用程序.js:

import store from './store';
import { Provider } from 'react-redux';
import App from './app.jsx';
window.addEventListener('load', (event) => {
    ReactDOM.render(
        <Provider store={store}>
            <App/>
        </Provider>,
        document.querySelector('#app-root')
    );
});

应用程序.jsx:

import React from 'react';
import store from './store';
import '../vendor/paho-mqtt/mqttws31.js'; // the mqtt lib
const MQTT = window.Paho.MQTT; // the MQTT object

class App extends React.Component {
  
    state = {
      activeTab: 0,
      items: {}, //from redux
      wsClientStatus: 'Not Connected'
    }

    shouldComponentUpdate = (nextProps, nextState) => {
      const { wsClientStatus } = this.state
      if (wsClientStatus !== nextState.wsClientStatus) {
        return true
      }
      return false;
    }


    componentDidMount = () => {
        this.wsClientInit();
    }


    render () {
      //console.log('app render')
        const { wsClientStatus } = this.state; // for a status bar
        const state = this.state
        return (
           <DimmableLight topics={{DMX:{read:'DMX_0', write:'WEB/DMX_0'}}} bpTopic={"WEB/DI01"} location="WC" publish={this.wsClientPublish} />
        )
    }
   
    wsClient = new MQTT.Client("YOUR HOST", 9001, "myclientid_" + parseInt(Math.random() * 100, 10));

    wsClientSetCallBacks = () => {
      
      const that = this;
      this.wsClient.onConnectionLost = function (responseObject) {
        console.log("Ws client:: connexion lost...");
        that.setState({wsClientStatus: 'Not connected'});
        //reconnect
        that.wsClientConnect(that.wsClient);
      };

      this.wsClient.onMessageArrived = function (message) {
        //Do something with the push message you received
        var data = JSON.parse(message.payloadString);
        console.log("Received <- " + message.destinationName + ":: ", data);

        //update the store
        //only one topic / all topics
        if (Object.keys(data).length > 1)
          store.dispatch({type:'SET_ALL', data:data})
        else
          store.dispatch({type:'SET_ONE', key:Object.keys(data)[0], value:data[Object.keys(data)[0]]})
      };
    }
        
    wsClientInit = () => {
        this.wsClientSetCallBacks();
        this.wsClientConnect(this.wsClient);
        window.wsClientPublish = this.wsClientPublish; // to publish manualy within chrome console
    }
   
    wsClientConnect = (wsClient) => {
      const _this = this
      console.log("Ws client:: tentative de connexion...");
      _this.setState({wsClientStatus: 'Tentative de connexion'});
      wsClient.connect({
        timeout: 15,
        useSSL: true,
        userName: 'USER_NAME',
        password: 'USER_PASSWORD',
        //Gets Called if the connection has sucessfully been established
        onSuccess: function () {
          console.log("Ws client:: Connecté.");
         _this.setState({wsClientStatus: 'Connecté'});
         wsClient.subscribe('unipi_data/#', {qos: 0});
         wsClient.subscribe('WEB/#', {qos: 0});
         setTimeout(function() {this.wsClientPublish("getAllData", "unipi_system/data", 1);}, 1000);
        },
        //Gets Called if the connection could not be established
        onFailure: function (message) {
          console.log("Ws client:: La Connexion a échoué: " + message.errorMessage);
         setTimeout(function() {
           _this.wsClientConnect(wsClient);
         }, 1000);
        }
      });
    }

    wsClientPublish = (payload, topic, qos = 1) => {
      //Send your message (also possible to serialize it as JSON or protobuf or just use a string, no limitations)
      var message = new MQTT.Message(JSON.stringify(payload));
      message.destinationName = topic;
      message.qos = qos;
      this.wsClient.send(message);
      console.log("publish -> ", topic, payload, qos);
    }
}

export default App;

就我而言,DimmableLight.jsx:

import React from 'react';
import { connect } from 'react-redux'; //connect child to the redux store
import { withStyles } from '@material-ui/core/styles';

import Card from '@material-ui/core/Card';
import CardHeader from '@material-ui/core/CardHeader';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import Slider from '@material-ui/core/Slider';
import IconButton from '@material-ui/core/IconButton';
import RadioButtonCheckedIcon from '@material-ui/icons/RadioButtonChecked';

import Bulb from './Bulb' // an SVG


class DimmableLight extends React.Component {
    static defaultProps = {
        data: {},
        topics: {DMX:{read:'DMX_0', write:'WEB/DMX_0'}}, // override from props in App.jsx
        bpTopic: "WEB/DI01",
        location: 'Chambre'
    }
  
    state = {

    } 


    // IMPORTANT: update the component only when selected topics change
    shouldComponentUpdate = (nextProps, nextState) => {
        const { data } = this.props
        let shouldUpdate = false;
        data && Object.keys(data).map((key) => {
            if (data[key] !== nextProps.data[key])
                shouldUpdate = true
        })
        return shouldUpdate;
    }

    handleChange = (evt, value) => {
        const { publish, topics } = this.props; // publish passed from props in App.jsx
        publish(parseInt(value), topics.DMX.write);
    }

    onBpPressed = (evt) => {
        const { publish, bpTopic } = this.props
        publish(parseInt(1), bpTopic);
    }

    onBpReleased = (evt) => {
        const { publish, bpTopic } = this.props
        publish(parseInt(0), bpTopic);
    }


    render () {
        const { data, topics, location, classes } = this.props
        //console.log('render dimmable', location)
        return (
            <Card className={classes.root}>
                <CardHeader title={location}>
                </CardHeader>
                <CardContent className={classes.cardContent}>
                    <Bulb luminosity={(data[topics.DMX.read] || 0)/254}/>
                </CardContent>
                <CardActions className={classes.cardActions}>
                    <Slider min={0} max={254} value={data[topics.DMX.read] || 0} onChange={this.handleChange} aria-labelledby="continuous-slider" />
                    <IconButton color="primary" variant="contained" onMouseDown={this.onBpPressed} onMouseUp={this.onBpReleased}>
                        <RadioButtonCheckedIcon/>
                    </IconButton>
                </CardActions>
            </Card>
        )
    }

}



const styles = theme => ({
    root: { 
        [theme.breakpoints.down('sm')]: {
            minWidth: '100%',
            maxWidth: '100%',
        },
        [theme.breakpoints.up('sm')]: {
            minWidth: 180,
            maxWidth: 180,
        },
    },
    cardContent: {
        textAlign: 'center'
    },
    cardActions: {
        margin: '0px 10px 0px 10px',
        '& > :first-child': {
            margin: '0 auto'
        }
    }
    
  });


// Connect only wanted topics, set first in defaultProps to be sure to have them in ownProps
const mapStateToProps = (state, ownProps) => {
    let data = []
    Object.keys(ownProps.topics).map(topicKey => {
        data[ownProps.topics[topicKey].read] = state.items[ownProps.topics[topicKey].read] || 0
    });
      return {
          data: data
      }
}  

export default connect(mapStateToProps)(withStyles(styles, {withTheme: true})(DimmableLight))

推荐阅读