1

Currently i'm rewriting a class component to a function component. I need to do this since i need to use the useSelector hook from redux. Now i'm getting pretty close but i'm having some trouble with the json array getting mapped. It's letting me know it's not a function. In the fetch i'm logging the leaderboard which has returned. This gives me the json i was expecting.

[
  {
    "ID": 1,
    "teamName": "Developers",
    "time": "19:54"
  },
  {
    "ID": 1591621934400,
    "teamName": "h435hfg",
    "time": "19:54"
  }
]

Then here is my code that im having trouble with:

import React, {useEffect, useState} from 'react';
import '../style/App.scss';
import {useSelector} from "react-redux";

function Leaderboard() {

    const io = require('socket.io-client');
    const socket = io.connect("http://localhost:3001/", {
        reconnection: false
    });
    const [leaderboard, setLeaderboard] = useState([]);

    const timerState = useSelector(state => state.timerState);


    useEffect(() => {
        socket.emit("addTeamToLeaderboard", getTeam());

        fetch('http://localhost:3000/leaderboard')
            .then(response => response.json())
            .then(leaderboard => {
                leaderboard.push(getTeam()); // this is just so your team score renders the first time
                setLeaderboard({leaderboard})
                console.log(leaderboard)
            });
    }, [socket]);

    const getTeam = () => {
        let team = JSON.parse(sessionStorage.getItem('currentTeam')) ;
        team.time = timerState;
        return team;
    }

    const leaderboardElements = leaderboard.map((data, key) => {
        return (
            <tr key={key} className={ data.ID === getTeam().ID ? "currentTeam" : "" }>
                <td>{data.teamName}</td>
                <td>{data.time}</td>
            </tr>
        )
    })

    return (
            <div>
                <h1>Leaderboard</h1>
                <table className="leaderboard">
                    <tr>
                        <th>Team</th>
                        <th>Time</th>
                    </tr>
                    {leaderboardElements}
                </table>
            </div>
        );

}

export default Leaderboard;

The old code which im rewriting:

import React from 'react';
import '../style/App.scss';

class Leaderboard extends React.Component {
    state = {
        leaderboard: []
    }

    compare(a, b) {
        if (a.time < b.time) {
            return -1;
        }
        if (a.time > b.time) {
            return 1;
        }
        return 0;
    }

    getTeam(){
        let team = JSON.parse(sessionStorage.getItem('currentTeam')) ;
        team.time = 12.13; //Todo add actual playing time
        return team;
    }

    componentDidMount() {
        const io = require('socket.io-client');
        const socket = io.connect("http://localhost:3001/", {
            reconnection: false
        });

        socket.emit("addTeamToLeaderboard", this.getTeam());

        fetch('http://localhost:3000/leaderboard')
            .then(response => response.json())
            .then(leaderboard => {
                leaderboard.push(this.getTeam()); // this is just so your team score renders the first time
                this.setState({ leaderboard })
            });
    }

    render() {
        return (
            <div>
                <h1>Leaderboard</h1>
                <table className="leaderboard">
                    <tr>
                        <th>Team</th>
                        <th>Time</th>
                    </tr>
                    {
                        this.state.leaderboard.sort(this.compare).map((data, key) => {
                            return (
                                <tr key={key} className={ data.ID == this.getTeam().ID ? "currentTeam" : "" }>
                                    <td>{data.teamName}</td>
                                    <td>{data.time}</td>
                                </tr>
                            )
                        })
                    }
                </table>
            </div>
        );
    }

}

export default Leaderboard;
1
  • you should not use socket in useEffect like [socket], it only reruns if socket changes, so the reference. Commented Jun 8, 2020 at 14:40

2 Answers 2

1

I'm not following why you are changing leaderboard data type. If it is an array you shouldn't do setLeaderboard({leaderboard}) because you are assigning an object to the state.

You should pass a new array to the setLeaderboard like:

setLeaderboard([...leaderboard]);

Also if you do

setLeaderboard([...leaderboard]);
console.log(leaderboard);

You will not get the updated state right in the log, because set state is an asynchronous call.

Another tip, I would highly recommend you to put the socket connection not in the useEffect function, put outside the functional component.

const io = require('socket.io-client');
const socket = io.connect("http://localhost:3001/", {
  reconnection: false
});

function Leaderboard() {
  ...
}
Sign up to request clarification or add additional context in comments.

Comments

0

It's letting me know it's not a function

/* fetch data */

leaderboard.push(getTeam()); 
setLeaderboard({leaderboard}) // => change to setLeaderboard(leaderboard.concat(getTeam()))
console.log(leaderboard)

/* other functions below */

the difference between setState and the setLeaderboard that is returned from useState is that (when giving none callback argument)

  • setState expects an object with {[key: stateYouAreChanging]: [value: newState],
  • setLeaderboard expects the newStatValue as the argument.

So your code above is setting leaderboard state to be an object with that looks like this

leaderboard = {
  leaderboard: NEW_LEADERBOARD_FETCHED_FROM_REQUEST
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.