Как получить состояние, которое исходит из реквизита, который получен из API в React

#reactjs #asynchronous #react-props #setstate

#reactjs #асинхронный #react-props #setstate

Вопрос:

Что я хочу сделать

  • При первом рендеринге дочернего компонента я хотел бы использовать значение в реквизите из родительского компонента

Проблема

  • Когда дочерний компонент сначала отображается, реквизит не устанавливается в состояние в дочернем компоненте

Я новичок React . Я пытаюсь использовать props для вызова API с помощью axios in componentDidMount в дочернем компоненте. Я имею в виду, что я вызываю API в родительском компоненте и устанавливаю данные из этого API в дочерний компонент как props .

Однако, когда я пытаюсь это сделать, props оно не установлено state в дочернем компоненте.

Например, когда я извлекаю продукт, у которого есть некоторые category , я набираю localhost:3000/api/parent/?category=1/ . Но, мой console.log показывает мне localhost:3000/api/parent/?category=undefined , потому что я предполагаю, что реквизит не задан при первом рендеринге дочернего компонента. На самом деле, я вижу category объект, state как показано ниже.

введите описание изображения здесь

Я думаю props , оно полностью установлено state после завершения первого рендеринга дочернего компонента.

Как я мог бы установить props , что исходит из API state ? Хотя я перепробовал много решений, которые я нашел в stackoverflow, я так долго застрял в этой проблеме.

Я хотел бы, чтобы вы рассказали мне о некоторых решениях.

Большое вам спасибо.

== == ==

Мой код выглядит следующим образом.

Родительский компонент

 class Top extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loginUser: '',
      categories: [],
    };
  }
  async componentDidMount() {
    const localhostUrl = 'http://localhost:8000/api/';
    const topCategoryList = ['Smartphone', 'Tablet', 'Laptop'];
    let passCategoryToState=[]
    axios
      .get(localhostUrl   'user/'   localStorage.getItem('uid'))
      .then((res) => {
        this.setState({ loginUser: res.data });
      })
      .catch((err) => console.log(err));

    await Promise.all(
      topCategoryList.map(async (category) => {
        await axios.get(localhostUrl   'category/?name='   category).then((res) => {
          passCategoryToState=[...passCategoryToState, res.data]
          console.log(passCategoryToState);
        });
      })
    );

    this.setState({categories : passCategoryToState})
  }

  render() {
    if (!this.props.isAuthenticated) {
      return <p> Developing now </p>;
    }
    if (this.state.loginUser === '' ) {
      return <CircularProgress />;
    } else {
      return (
        <>
          <Header loginUser={this.state.loginUser} />
          <Give_Item_List
            axiosUrl="http://localhost:8000/api/"
            subtitle="Smartphone Items"
            loginUser={this.state.loginUser}
            category={this.state.categories[0]}
          />
        </>
      );
    }
  }
}

export default Top;
  

И дочерний компонент

 class Give_Item_List extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: false,
      loginUser: this.props.loginUser,
      category: this.props.category,
    };
  }

  async componentDidMount() {
    let pickedGiveItems;
    await this.setState({ loading: true });
    await axios
      .get(this.props.axiosUrl   'giveitem/?category='   this.state.category.id)
      .then((res) => {
        pickedGiveItems = res.data;
        console.log(pickedGiveItems);
      })
      .catch((err) => console.log('Not found related to Items'));
    this.setState({ loading: false });
  }

  render() {
    if (this.state.loading == true) {
      return <CircularProgress />;
    }
    return <h1>Give_Item_List</h1>;
  }
}

export default Give_Item_List;

  

==============

Редактировать: изменить на componentDidUpdate

 componentDidUpdate(prevProps) {
  if(prevProps.category != this.props.category){
    let pickedGiveItems;
    this.setState({ loading: true });
    axios
      .get(this.props.axiosUrl   'giveitem/?category='   this.props.category.id)
      .then((res) => {
        pickedGiveItems = res.data;
        console.log(pickedGiveItems);
      })
      .catch((err) => console.log('NotFount'));
    this.setState({ loading: false });
  }
}
  

Это все еще не работает…

Ответ №1:

В конструкторе this.props значение не определено, но вы можете получить доступ к реквизиту напрямую.

 constructor(props) {
  super(props);
  this.state = {
    loading: false,
    loginUser: props.loginUser,
    category: props.category,
  };
}
  

Однако теперь я должен отметить, что хранение реквизита в состоянии является обычным анти-шаблоном react, вы всегда должны использовать значения реквизита this.props там, где они вам нужны.

Например:

 async componentDidMount() {
  let pickedGiveItems;
  await this.setState({ loading: true });
  await axios
    .get(this.props.axiosUrl   'giveitem/?category='   this.props.category.id)
    .then((res) => {
      pickedGiveItems = res.data;
      console.log(pickedGiveItems);
    })
    .catch((err) => console.log('Not found related to Items'));
  this.setState({ loading: false });
}
  

Вы также не можете await обновить состояние реакции, поскольку оно не является асинхронной функцией и не возвращает обещание. Просто укажите начальное истинное состояние загрузки и переключите false, когда запрос на выборку разрешится.

 class Give_Item_List extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: true
    };
  }

  async componentDidMount() {
    let pickedGiveItems;
    
    await axios
      .get(this.props.axiosUrl   'giveitem/?category='   this.props.category.id)
      .then((res) => {
        pickedGiveItems = res.data;
        console.log(pickedGiveItems);
      })
      .catch((err) => console.log('Not found related to Items'));
    this.setState({ loading: false });
  }

  render() {
    if (this.state.loading) {
      return <CircularProgress />;
    }
    return <h1>Give_Item_List</h1>;
  }
}
  

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

1. Спасибо за ваш ответ. Я пробовал то, чему меня учили, но это не работает. Console.log все еще говорит cateogry.id , что есть undefined

2. @Toshi023Yuki Где ты регистрируешься category.id ? categories в родительском компоненте изначально находится пустой массив, поэтому this.state.categories[0] передача дочернему компоненту будет неопределенной при первоначальном рендеринге. Если вам нужно отреагировать на изменение category реквизита, вам также следует реализовать componentDidUpdate и выполнить выборку в дочернем компоненте при category.id изменениях.

3. Честно говоря, я никогда не использовал componentDidUpdate . Я пытался использовать его так, как вы меня учили, но это не работает. Я добавляю свой новый код componentDidUpdate к своему вопросу. Если с вами все в порядке, я бы хотел, чтобы вы сказали, что что-то не так.

4. @Toshi023Yuki Не беспокойтесь. Можете ли вы попробовать создать codesandbox своего кода, который воспроизводит проблему, и поделиться здесь? Может быть проще отладить код в реальном времени, чтобы увидеть, как реквизит может меняться или не меняться со временем.

5. Спасибо за отличную поддержку. Я создал новую песочницу. Честно говоря, я не могу сказать, что я создал его правильно, но я поделюсь с вами ссылкой. Это мой codesandbox . Вам может потребоваться пароль, поэтому я расскажу его вам. username является гостевым и password имеет значение password000. Возможно, у вас проблемы, потому что я создаю это приложение на японском языке. Если вы чувствуете неудобства, мне очень жаль.