redux-utilities/redux-actions

Does state changing is async?

tonoyandev opened this issue · 3 comments

I have React component, which connects to WebSocket server inside componentDidMount.

componentDidMount() {
    const { connectWs } = this.props
    connectWs(
        true,
        () => {},
        () => {},
        res => {
            this.socketCallback(res)
        },
    )
}

socketCallback = payload => {
    const { handleClaimSuccess } = this.props
    const { horse_id, race_id } = payload
    const data = {
        'horseId': horse_id,
        'raceId': race_id
    }

    handleClaimSuccess(data)
}

render() {
    const { claimed } = this.props
    return (
        <div>
            {claimed.map((data, index) => (
                <div className="primary-text">
                     { data.isPending ? 'PENDING' : 'COLLECTED' }
                </div>
            )}
        <div>
    )
}

The server sends 3 messages via sockets, which component handles and sends to reducer for updating the state. The time difference between that 3 messages is 30-60 microsecond.

'handleClaimSuccess': (state, { payload }) => {
    if (payload) {
        const { horseId, raceId } = payload
        const updatedWinnings = state.userWinnings

        const indexOfWinningInClaimed = findIndex(updatedWinnings.claimed, {
            'horseId': horseId,
            'race': {
                'raceId': raceId
            },
        })

        updatedWinnings.claimed[indexOfWinningInClaimed].isPending = false

        return {
            ...state,
            'userWinnings': updatedWinnings
        }
    }

    return state
}

All messages change the state accordingly, but component renders results only once (after 1st message) and ignore next 2 messages.

As I understand its related to small interval of messages, cause it works fine with single messages.

How it can handle all 3 messages and render the state after each message?

I don't have a whole answer for you, only one part.

Your reducer is mutating some of the state, instead of recreating it all. You have this line:

updatedWinnings.claimed[indexOfWinningInClaimed].isPending = false

... which changes the value of isPending on one item from a collection. However, you are not then making a new item with the new property value; you are returning the same item with the value mutated. Also, you are not returning a new .claimed array; only the same array with one property of one member changed. Because of this, the store->selectors->connect()->props->shouldComponentUpdate() sections of the component lifecycle will (probably) not notice that updatedWinnings.claimed[indexOfWinningInClaimed] has changed.

You could try modifying this reducer to make a new updatedWinnings.claimed array, with a new [indexOfWinningInClaimed] item, with the new .isPending value.

My preference would be to extract more and more specialised reducers. The one you have now operates on the collection. Make a new one which operates on an item in the collection (handling any actions it may need). You might need to go deeper still if the other data in updatedWinnings.claimed[indexOfWinningInClaimed] is rich and nested.

(This is likely not a problem with redux-actions. You might get better answers if you ask again on StackOverflow with a [redux] tag. However, redux-actions can help you: its handleActions() function makes it much easier to create layers of reducers with pass smaller and smaller pieces of state, and the action, to one another.)

@simondell thanks, yes that was not related to redux-actions. Closed.