Redux在异步函数中存储更新,但容器不会获得道具更改

时间:2018-04-11 08:35:07

标签: reactjs asynchronous redux react-redux

我试图使用react,redux和redux-thunk创建一个实时应用程序,通过sockJS将STOMP从后端通过套接字获取对象,并在每次对象到达时更新redux存储并最终更新容器当redux存储更新时。

我的连接类通过stomp over sockjs就是这个;

class SearcButtons extends Component {
    render() {
        return (
            <div className="searchbuttons">
               <RaisedButton className="bttn" label="Start" onClick={() => this.start_twitter_stream()} />
               <RaisedButton  className="bttn" label="Start" onClick={() => this.stop_twitter_stream()} />
            </div>
        );
    }


   start_twitter_stream() {
        let stompClient = null;
        var that = this;
        let socket = new SockJS('http://localhost:3001/twitterStream');
        stompClient = Stomp.over(socket);
        stompClient.debug = null;
        stompClient.connect({}, function () {
            stompClient.subscribe('/topic/fetchTwitterStream', function (tokenizedTweet) {
                let tweet = JSON.parse(tokenizedTweet.body);
                    let payload = {
                        data: {
                            tweets: that.props.state.reducer.tweets,
                        }
                    }
                    payload.data.tweets.push(
                        {
                            "username": tweet.username,
                            "tweet": tweet.tweet,
                        }
                    );
                    that.props.actions.update_tweets_data(payload);

            });
            stompClient.send("/app/manageTwitterStream", {}, JSON.stringify({ 'command': 'start', 'message': that.props.state.reducer.keyword }));
            let payload = {
                data: {
                    socketConnection: stompClient
                }
            }
            that.props.actions.start_twitter_stream(payload);

        });

    }
    stop_twitter_stream() {
        var socketConnection = this.props.state.reducer.socketConnection;
        socketConnection.send("/app/manageTwitterStream", {}, JSON.stringify({ 'command': 'stop', 'message': null }));
        socketConnection.disconnect();
        let payload = {
            data: {
                socketConnection: null
            }
        }
        return this.props.actions.stop_twitter_stream(payload);
    }

}

SearcButtons.propTypes = {
    actions: PropTypes.object,
    initialState: PropTypes.object
};

function mapStateToProps(state) {
    return { state: state };
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(actions, dispatch)
    };
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(SearcButtons);

我在App.js中调用tweet面板容器

import TweetPanel from './containers/TweetPanel';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';

class App extends Component {

    render() {
        return (
            <MuiThemeProvider>
                <div className="main">
                    <TweetPanel />
                </div>
            </MuiThemeProvider>

        );
    }

}

export default App;

我的侦听redux-store的容器是这样的;

class TweetPanel extends Component {
   const TABLE_COLUMNS = [
       {
           key: 'username',
           label: 'Username',
       }, {
           key: 'tweet',
          label: 'Tweet',
       },
   ];
render() {
        console.log(this.props);
         return (
            <DataTables
                height={'auto'}
                selectable={false}
                showRowHover={true}
                columns={TABLE_COLUMNS}
                data={   
                    (typeof (this.props.state.reducer.tweets) !== "undefined" ) ?this.props.state.reducer.tweets : []
                }
                showCheckboxes={false}
                onCellClick={this.handleCellClick}
                onCellDoubleClick={this.handleCellDoubleClick}
                onFilterValueChange={this.handleFilterValueChange}
                onSortOrderChange={this.handleSortOrderChange}
                page={1}
                count={100}
            />
        );


        }
    }
TweetPanel.propTypes = {
    actions: PropTypes.object,
    initialState: PropTypes.object
};

function mapStateToProps(state) {
    return { state: state };
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(actions, dispatch)
    };
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(TweetPanel);

我的行动;

import {
    BUILD_TWITTER_STREAM,
    START_TWITTER_STREAM,
    UPDATE_TWEETS_DATA,

} from '../actions/action_types';

export function build_twitter_stream(state) {
    return {
        type: BUILD_TWITTER_STREAM,
        payload: state
    };
}
export function start_twitter_stream(state) {
    return {
        type: START_TWITTER_STREAM,
        payload: state
    };
}
export function update_tweets_data(state) {
    return {
        type: UPDATE_TWEETS_DATA,
        payload: state
    };

}

我的减速机;

import update from 'immutability-helper';

let initialState =  {

    socketConnection   : null,
    tweets             : [ ]         
}


export default function reducer(state = initialState, action) {

    switch (action.type) {

        case BUILD_TWITTER_STREAM:
            return update(
                state, {
                    socketConnection: { $set: action.payload.data.socketConnection }
                }
            );

        case START_TWITTER_STREAM:
            return update(
                state, {
                    socketConnection: { $set: action.payload.data.socketConnection }
                }
            );
        case UPDATE_TWEETS_DATA:
            return update(
                state, {
                    tweets: { $merge: action.payload.data.tweets }
                }
            );
        default:
            return state;
    }
}

我的观察是当我尝试通过stock over Sockjs连接到socket时,我需要将上下文作为名为“that”的变量传递,您可以看到上面的第一个代码块,并在stompClient的connect函数中使用该上下文更新redux store回调,这意味着我在asynchronou函数中更新存储,当我查看Chrome的Redux devtools扩展时,redux存储更新很好,但是容器不会更新,除非我按下停止按钮触发非异步操作

在此先感谢,非常感谢您的帮助:)

1 个答案:

答案 0 :(得分:1)

由于我遇到了类似的问题,因此我可以提供另一种方法,即函数委托方法。我在诸如RaisedButton之类的组件中创建道具。例如,我创建BindStore道具,例如:

<RaisedButton BindStore={(thatContext)=>{thatContext.state = AppStore.getState();}}... />

我还可以添加订阅者道具,例如:

<RaisedButton SubscribeStore={(thatContext) => {AppStore.subscribe(()=>{thatContext.setState(AppStore.getState())})}} ... />

在RaisedButton.js上,我可以轻松地提供thatContext:

...
constructor(props){
        super(props);

        if(this.props.BindStore){
          this.props.BindStore(this);
        }

        if(this.props.SubscribeStore){
          this.props.SubscribeStore(this);
        }

      }
...

这样做也意味着通过使用道具,一个RaisedButton可能不具有BindingStore功能或SubscribeStore功能。我还可以通过道具来呼叫商店调度员,例如:

 in parent.js:
 <RaisedButton PropsClick={(thatContext, thatValue) => {AppStore.dispacth(()=> 
  {type:"ActionType", payload:{...}})}} ... />

in RaisedButton.js
//as an example I used here dropDown, which is:
import { Dropdown } from 'react-native-material-dropdown'; 
//react-native-material-dropdown package has issues but the solutions are in internet :)
...
render(){
  return(
     <View>
        <Dropdown onChangeText={(value) => {this.props.PropsClick(this, value);}}  />
     </View>
  )
}
...

在许多示例中,例如您的父级是SearchButtons,必须渲染父级,父级必须订阅商店,因此,当任何子项更改商店时,将重新呈现所有组件群集。但是,通过这种方法,子组件将被订阅并绑定到商店。甚至一个孩子都可以调度一个动作,然后,其他相同类型的孩子订阅的函数将被回调,只有被订阅的孩子才被重新呈现。同样,您将仅将一个组件(即父组件)连接到redux存储。

//parent.js
import { connect } from 'react-redux'; 
import AppStore from '../CustomReducer';
...

//you will not need to set your parent's state to store state. 

我并未对此方法进行过多研究,但未使用映射功能。但是,子组件将访问所有商店数据,而且在映射中,所有商店数据也都可以访问。