0

there is such a task

When hovering over a selected month, display a list of people who were born this month. Already tried without .bind but still have an error

import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import './styles.css';
import classes from './components/Month/month.module.css'


function App() {
    // state = {
    //   users: false
    // }
    const [users, setUser] = useState(null);
    const usersVisible = false;

    const fetchData = async () => {
        const response = await axios.get(
            'https:api/task0/users'
        );

        setUser(response.data);
    };

    const groupedUsers = users && users.reduce((acc, n) => {


        acc[new Date(n.dob).getMonth()].users.push(n);
        return acc;

    }, [...Array(12)].map((n, i) => ({
        month: new Date(0, i).toLocaleString('ru-RU', { month: 'long' }),
        users: []



    }))
    );

    const onHover = () => {
        return usersVisible = true;
    }
    console.log(usersVisible);
    return (
        <div className="App">
            <h1>Users list</h1>


            {/* Fetch data from API */}
            <div>
                <button className="fetch-button" onClick={fetchData}>
                    download users
        </button>
                <br />
            </div>

            {/* Display data from API */}
            {}
            <div className="users">
                {groupedUsers && groupedUsers.map(n => (
                    <div id="months" key={n.month} className={n.users > 0 ? classes.month.grey : n.users > 2 ?
                        classes.blue : n.users > 6 ? classes.green : classes.red} onMouseOver={(event) => this.onHover.bind(event)}>
                        <h2>{n.month}</h2>
                        {n.users.map(user => (
                            usersVisible ?

                                <div key={user.id} >

                                    <div>
                                        <h4 className={classes.userId}>user #{user.id}</h4>
                                        данные пользователя...</div>
                                </div> : null
                        ))}
                    </div>
                ))}
            </div>

        </div>
    );
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

Here is code of error

TypeError: Cannot read property 'onHover' of undefined
onMouseOver
C:/Users/Константин/Desktop/react/new-app/src/index.js:60
  57 |     <div className="users">
  58 |      {groupedUsers && groupedUsers.map(n => (
  59 | <div id="months" key={n.month}  className={n.users > 0 ? classes.month.grey  : n.users > 2 ? 
> 60 |       classes.blue : n.users > 6 ? classes.green : classes.red} onMouseOver={(event) => this.onHover.bind(event)}>
     | ^  61 |   <h2>{n.month}</h2>
  62 |   {n.users.map(user => (
  63 |     usersVisible ? 

the function onHover changes the value of the variable, which should lead to the output of the block in jsx. Where is the problem hiding? thanks a lot!!

Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113
  • There's no `this` in function components. It's then useless to use `bind`, and even more useless if you're wrapping it with an arrow function `(event) => this.onHover.bind(event)` and using `bind` like that doesn't call the function, it just returns a new one which would have `event` as the `this` value. – Emile Bergeron Jan 09 '20 at 18:34
  • To learn more about `this` (context), see ["How to access the correct `this` inside a callback?"](https://stackoverflow.com/q/20279484/1218980) – Emile Bergeron Jan 09 '20 at 18:38
  • Then, for passing function to components, see the [official React documentation](https://reactjs.org/docs/faq-functions.html). – Emile Bergeron Jan 09 '20 at 18:39
  • thanks for you time and this info! Just tried every silly thing – Владислав Коперников Jan 09 '20 at 18:49

1 Answers1

2

Source of the error: As you are working on a function component, this keyword is not a reference to your component, but to the window. So in order to access, your onHover you just call it directly, not with the use of this or bind.

Also while this show/hide is relevant to a state that your component will have, usersVisible should be handled by state.

function App() {
  const [users, setUser] = useState(null);
  const [usersVisible, setVisibility] = useState(false);

  const fetchData = async () => {
    const response = await axios.get(
      'https://yalantis-react-school.herokuapp.com/api/task0/users'
    );

    setUser(response.data);
  };

  const groupedUsers = users && users.reduce((acc, n) => {
    acc[new Date(n.dob).getMonth()].users.push(n);
    return acc;
  }, [...Array(12)].map((n, i) => ({
    month: new Date(0, i).toLocaleString('ru-RU', { month: 'long' }),
    users: []
  })));

  return (
    <div className="App">
      <h1>Users list</h1>

      {/* Fetch data from API */}
      <div>
        <button className="fetch-button" onClick={fetchData}>
          download users
        </button>
        <br />
      </div>

      {/* Display data from API */}
      {}
      <div className="users">
       {groupedUsers && groupedUsers.map(n => (
  <div id="months" key={n.month}  className={n.users > 0 ? classes.month.grey  : n.users > 2 ? 
        classes.blue : n.users > 6 ? classes.green : classes.red} 
onMouseLeave={() => setVisibility(false)}
onMouseOver={() => setVisibility(true)}>
    <h2>{n.month}</h2>
    {n.users.map(user => (
      usersVisible ? 

      <div key={user.id} >

        <div>
         <h4 className={classes.userId}>user #{user.id}</h4>
        данные пользователя...</div>
      </div> : null
    ))}
  </div>
))}
      </div>
    </div>
  );
}

Finally, I've noticed that you are fetching data for your component, so consider using useEffect hook, as it's the recommented approach for data fetching in a function component

Michalis Garganourakis
  • 2,872
  • 1
  • 11
  • 24
  • Thanks for the detailed answer and understanding. from lack of understanding i've just tried all the options. I will take your advice! one more question: how to show only the content of the component that the mouse is over? Now when you hover, all components open and close immediately. – Владислав Коперников Jan 09 '20 at 19:04
  • Then you should keep separately for each month if it's visible or not. You could either add an extra property on your users object and show/hide based on this, or keep the visibility state in a separate array. I've created an example with a separate array of 12 items (1 for each month), changing it to true/false based on `onHover/onMouseLeave` event. https://codesandbox.io/s/optimistic-perlman-pvk6p – Michalis Garganourakis Jan 09 '20 at 19:44
  • If this solves your problem, consider marking the answer as accepted by clicking on the check mark beside the answer to toggle it from greyed out to filled in. In that way, you help others with similar issues find their way :) – Michalis Garganourakis Jan 09 '20 at 19:47