This.props не определено при повторной визуализации компонента

#javascript #reactjs #react-props

#javascript #reactjs #реагировать-props

Вопрос:

У меня возникла проблема в этом компоненте с props. Сначала реквизит не был определен при монтировании, но после обновления страницы он работал.

Я читал, что это произошло из-за того, что данные были извлечены после монтирования, поэтому я исправил это с помощью условия при рендеринге. Однако теперь у меня противоположная ошибка, и я не могу найти никакого решения, которое бы работало для меня. Реквизит работает нормально при монтировании, но при обновлении я получаю неопределенный.

У кого-нибудь есть идеи, почему это происходит?

Вот мой код: Profile

 import React from "react";
import axios from "axios";
import { Card, CardBody, CardTitle, CardText} from "reactstrap";
import Withuser from "./Withuser"

class Profile extends React.Component {
  constructor(props) {
    super(props)
    console.log(props)
    this.state = {
      thoughts: [],
    }
  }

  componentDidMount = () => {
    this.getShares()
  }

  getShares = () => {
    console.log(this.props.user[0].id)
    const user_id = this.props.user[0].id

    axios(`http://localhost:7001/api/profile/shares/${user_id}`)

    .then(res => {
      console.log(res.data)
      this.setState(state => ({
        thoughts: res.data,
        loggedIn: !state.loggedIn
      }))
    })
    .catch(error => {
      this.setState({ error: true })
    })
  }

  render() {
    const { thoughts } = this.state
    if (!thoughts.length === 0) {
      return <div />
    }
    return(
    <div>
      <h1>Your posts</h1>
      <ul>
        {thoughts.map((thought, index) => {
          return (
            <Card className='thoughts' key={index}>
              <CardBody>
                <CardTitle>{thought.user_name} posted at {thought.createdAt}</CardTitle>
                <CardText>{thought.body}</CardText>
              </CardBody>
            </Card>
          )
        })}
      </ul>
    </div>
    ) 
  }
}


export default Withuser(Profile);  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>  

Whithuser

 import React, { useState, useEffect } from 'react'
import axios from 'axios'

const withUser = (Component, options = { renderNull: true }) => props => {
  const [userData, setUserData] = useState(null)
  const [userId, setUserId] = useState(null)
  const [error, setError] = useState(false)
  //const [loading, setLoading] = useState(false)

  useEffect(() => {
    const token = localStorage.getItem('token')
    if (!token) return
    //setLoading(true)
    axios('http://localhost:7001/api/profile', {
      headers: {
        'x-access-token': token,
      },
    })
      .then(response => {
        const id = response.data.id
        setUserId(id)
      })
      .catch(error => {
        setError(true)
        console.log(error)
      })
      {/*}.finally(() => {
        setLoading(false)
      })*/}
  }, [])

  useEffect(() => {
    //setLoading(true)
    axios(`http://localhost:7001/api/users/${userId}`)
      .then(response => {
      
      setUserData(response.data)
    })
    {/*}.finally(() => {
      setLoading(false)
    })*/}
  }, [userId])
  
  //if(loading) return null;
  if (!userData amp;amp; options.renderNull) return null
  return <Component {...props} user={userData} />
}

export default withUser  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>  

Комментарии:

1. Каково точное сообщение об ошибке?

2. Привет, @Benjamin, я получаю два «Неперехваченная ошибка типа: не удается прочитать свойство ‘id’ неопределенного» и «Неперехваченный (в обещании) Ошибка типа: не удается прочитать свойство «id» неопределенного», оба одновременно.

Ответ №1:

Вот как я бы реорганизовал этот код.

Во-первых, внутри вашего withUser HOC, вместо двух useEffect хуков, я бы объединил работу в один useEffect , поэтому вы получаете начальный рендеринг с props.user.id является неопределенным.

Я бы также очистил запросы axios в асинхронные функции для удобства чтения.

С пользователем

 import React, { useState, useEffect } from 'react';
import axios from 'axios';

const fetchProfile = async () => {
  const token = localStorage.getItem('token');

  if (!token) {
    throw new Error('Missing Token');
  }

  const response = await axios('http://localhost:7001/api/profile', {
    headers: {
      'x-access-token': token,
    },
  });

  const profile = response.data;

  return profile;
};

const fetchUsers = async (userId) => {
  const response = await axios(`http://localhost:7001/api/users/${userId}`);
  const users = response.data;
  return users;
};

const withUser = (Component, options = { renderNull: true }) => (props) => {
  const [userData, setUserData] = useState();

  useEffect(() => {
    async function loadUser() {
      try {
        const profile = await fetchProfile();
        const users = await fetchUsers(profile.id);
        setUserData(users);
      } catch (error) {
        console.error(error);
      }
    }
    loadUser();
  }, []);

  if (userData === undefined amp;amp; options.renderNull === true) {
    return null;
  }

  return <Component {...props} user={userData} />;
};

export default withUser;
  

Тогда в компоненте Profile я бы не стал сильно меняться, кроме рефакторинга getShares() в асинхронную функцию. А затем небольшая очистка здесь и там.

Профиль

 import React from 'react';
import axios from 'axios';
import { Card, CardBody, CardTitle, CardText } from 'reactstrap';
import withUser from './Withuser';

class Profile extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      error: false,
      loggedIn: undefined,
      thoughts: [],
    };
  }

  componentDidMount = () => {
    this.getShares();
  };

  getShares = async () => {
    const userId = this.props.user[0].id;

    try {
      const response = await axios(`http://localhost:7001/api/profile/shares/${userId}`);
      this.setState((state) => ({
        thoughts: response.data,
        loggedIn: !state.loggedIn,
      }));
    } catch (error) {
      this.setState({ error: true });
    }
  };

  render() {
    const { thoughts } = this.state;

    if (!(thoughts.length > 0)) {
      return null;
    }

    return (
      <div>
        <h1>Your posts</h1>
        <ul>
          {thoughts.map((thought, index) => {
            return (
              <Card className="thoughts" key={index}>
                <CardBody>
                  <CardTitle>
                    {thought.user_name} posted at {thought.createdAt}
                  </CardTitle>
                  <CardText>{thought.body}</CardText>
                </CardBody>
              </Card>
            );
          })}
        </ul>
      </div>
    );
  }
}

export default withUser(Profile);