2

I am having some problems with the dispatch of an action under Redux.

I have applied the different tutorials to the letter, but yet this does not take, I always get an empty table in my props.

Here is my code:

Config.js

import React from "react"
import ReactDOM from "react-dom"

import { Provider } from 'react-redux'
import store from './store'

import ConfigPage from "./components/ConfigPage/ConfigPage"

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={store}>
    <ConfigPage />
  </Provider>,
  rootElement
);

Store

    import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'

import rootReducer from './reducers'

import { fetchWalletAddress } from './actions/index'


const store = createStore(rootReducer, applyMiddleware(thunk))

store.dispatch(fetchWalletAddress());

export default store;

Reducer wallet.js

import { GET_WALLET_ADDRESS } from "../actions/actionTypes.js";

const initialState = {
    wallet:[]
}

export default function(state = initialState, action){
    switch(action.type){
        case GET_WALLET_ADDRESS:
            return [ ...state, ...action.payload];
        default: 
            return state;
    }
}

My action :

import {GET_WALLET_ADDRESS} from './actionTypes.js'
import axios from 'axios'

const apiUrl = 'https://api.elrond.com/accounts/erd15qltd5ccalm5smmgdc5wnx46ssda3p32xhsz4wpp6usldq7hq7xqq5fmn6';


export const fetchWalletAddress = () => {
    return (dispatch) => {
        return axios.get(apiUrl)
            .then(response => {
                return response.data
            })
            .then(data => {
                dispatch({
                    type: GET_WALLET_ADDRESS,
                    payload: data
                })
            })
            .catch(error => {
                throw (error);
            });
    };
};

And for finish, my Configpage.js

import React from 'react'
import Authentication from '../../util/Authentication/Authentication'
import './Config.css'
import { connect } from 'react-redux'

import { fetchWalletAddress } from '../../actions/index'

class ConfigPage extends React.Component{
    constructor(props){
        super(props)
        this.Authentication = new Authentication()

        //if the extension is running on twitch or dev rig, set the shorthand here. otherwise, set to null. 
        this.twitch = window.Twitch ? window.Twitch.ext : null
        this.state={
            finishedLoading:false,
            theme:'light',
            isVisible:true,
            wallet_address:'erd15qltd5ccalm5smmgdc5wnx46ssda3p32xhsz4wpp6usldq7hq7xqq5fmn6'
        }
        this.walletAddressHandler = this.walletAddressHandler.bind(this);
        this.onSubmitForm = this.onSubmitForm.bind(this);
    }

    walletAddressHandler(event){
        this.setState({
            [event.target.name]:event.target.value
        });
    }

    onSubmitForm(){
        fetchWalletAddress();
        this.twitch.rig.log(this.props.wallet)
    }

    contextUpdate(context, delta){
        if(delta.includes('theme')){
            this.setState(()=>{
                return {theme:context.theme}
            })
        }
    }

    visibilityChanged(isVisible){
        this.setState(()=>{
            return {
                isVisible
            }
        })
    }

    componentDidMount(){

        this.twitch.rig.log(this.props.wallet)

        if(this.twitch){
            this.twitch.onAuthorized((auth)=>{
                this.Authentication.setToken(auth.token, auth.userId)
                if(!this.state.finishedLoading){
                    // if the component hasn't finished loading (as in we've not set up after getting a token), let's set it up now.

                    // now we've done the setup for the component, let's set the state to true to force a rerender with the correct data.
                    this.setState(()=>{
                        return {finishedLoading:true}
                    })
                }
            })

            this.twitch.listen('broadcast',(target,contentType,body)=>{
                this.twitch.rig.log(`New PubSub message!\n${target}\n${contentType}\n${body}`)
                // now that you've got a listener, do something with the result... 

                // do something...

            })

            this.twitch.onVisibilityChanged((isVisible,_c)=>{
                this.visibilityChanged(isVisible)
            })

            this.twitch.onContext((context,delta)=>{
                this.contextUpdate(context,delta)
            })
        }
    }

    componentWillUnmount(){
        if(this.twitch){
            this.twitch.unlisten('broadcast', ()=>console.log('successfully unlistened'))
        }
    }

    filterFloat(value) {
        if (/^(\-|\+)?([0-9]+(\.[0-9]+)?|Infinity)$/
          .test(value))
          return Number(value);
      return NaN;
    }
    
    render(){
        if(this.state.finishedLoading && this.state.isVisible){
            return (
                <div className="App">
                    <div className={this.state.theme === 'light' ? 'App-light' : 'App-dark'} >
                        <p>Add your wallet address</p>

                        <input
                            name="wallet_address"
                            type="text"
                            onChange={this.walletAddressHandler}
                            value={this.state.wallet_address}>
                        </input>
                        <p>{this.props.wallet.username}</p>
                        <button OnClick={this.onSubmitForm}>Try it</button>
                        <ul>
                            {this.state.wallet ? String((Number(this.state.wallet.balance) * Math.pow(10, -18)).toFixed(4)) : null}
                        </ul>
                    </div>
                </div>
            )
        }else{
            return (
                <div className="App">
                </div>
            )
        }

    }
}


const mapStateToProps  = state => {
    return {
        wallet: state.wallet
    }
};

export default connect(mapStateToProps, null)(ConfigPage);

thank you in advance for your help

Gordo
  • 21
  • 3
  • This line isn’t right: return [ ...state, ...action.payload]. Your state is an object with a property wallet. It is not an array. You probably want something like return { ...state, wallet: action.payload } depending on what type the data in the payload is. Is it an array? Are we entirely replacing the state.wallet array or merging into it? Should state.wallet even be an array? – Linda Paiste Feb 28 '21 at 10:34
  • I wanted to retrieve the API data as an object in a single array. Or just an object. API return an object like that :{"address":"erd15qltd5ccalm5smmgdc5wnx46ssda3p32xhsz4wpp6usldq7hq7xqq5fmn6","nonce":873,"balance":"16386430200173352249771","txCount":2915} – Gordo Feb 28 '21 at 10:39

1 Answers1

2

You do not need to dispatch fetchWalletAddress while creating store, you can do that in the component. Also when you call fetchWalletAddress in the component, make sure to use the function that you make available to component via mapDispatchToProps argument of connect otherwise it wouldn't affect the redux store

Another thing you must do is to not use the updated redux value in the same function call since it takes a render cycle for it to reflect the updated change

store.js

import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'

import rootReducer from './reducers'


const store = createStore(rootReducer, applyMiddleware(thunk))


export default store;

Configpage.js

...

componentDidMount(){
    this.props.fetchWalletAddress();
    this.twitch.rig.log(this.props.wallet)

    if(this.twitch){
        this.twitch.onAuthorized((auth)=>{
            this.Authentication.setToken(auth.token, auth.userId)
            if(!this.state.finishedLoading){
                // if the component hasn't finished loading (as in we've not set up after getting a token), let's set it up now.

                // now we've done the setup for the component, let's set the state to true to force a rerender with the correct data.
                this.setState(()=>{
                    return {finishedLoading:true}
                })
            }
        })

        this.twitch.listen('broadcast',(target,contentType,body)=>{
            this.twitch.rig.log(`New PubSub message!\n${target}\n${contentType}\n${body}`)
            // now that you've got a listener, do something with the result... 

            // do something...

        })

        this.twitch.onVisibilityChanged((isVisible,_c)=>{
            this.visibilityChanged(isVisible)
        })

        this.twitch.onContext((context,delta)=>{
            this.contextUpdate(context,delta)
        })
    }
}

...
componentDidUpdate(prevProps) {
    if (!_.isEqual(prevProps.wallet, this.props.wallet)) {
       this.twitch.rig.log(this.props.wallet)
    }
}
...

onSubmitForm(){
    this.props.fetchWalletAddress();// use action from props
}
...
const mapDispatchToProps = {
    fetchWalletAddress,
}
const mapStateToProps  = state => {
    return {
        wallet: state.wallet
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(ConfigPage);
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • Thanks for answer. I tried with your logic but i have simple blank page when i add this part : componentDidUpdate(prevProps) { if (!_.isEqual(prevProps.wallet, this.props.wallet)) { this.twitch.rig.log(this.props.wallet) } } And nothing in logs :s – Gordo Feb 28 '21 at 12:45
  • are you calling `this.props.fetchWalletAddress();` in componentDidMount – Shubham Khatri Feb 28 '21 at 12:53
  • Yes, i updated like this : componentDidUpdate() { this.twitch.rig.log(this.props.wallet.wallet.nonce) } and work fine now. Many thanks – Gordo Feb 28 '21 at 13:03