Axios получить ответ метода в React не может отображаться, получая данные из firebase в виде массива в моем приложении для блога

Интересно, может ли кто-нибудь мне помочь. Я прочитал много ответов StackOverflow по этой и другим замечательным статьям, таким как Вот этот, и пока не смог реализовать ответ.

У меня есть простое приложение для блога на React. У меня есть форма для отправки данных, а также отдельный компонент сообщений и сообщений. Я действительно могу отправлять данные в свою базу данных firebase. Я также получаю ответ в методе GET, но не могу показать ответ, как мне нужно. Мне нужен массив сообщений, у каждого из которых есть заголовок и контент, чтобы я мог отправлять его данные в свой компонент Post. Но я всегда получаю сообщение об ошибке типа (карта не может использоваться в ответе), и я фактически не могу получить массив из своей базы данных. Мне даже интересно, в правильном ли формате я отправляю данные. Пожалуйста, проверьте мой код ниже и помогите мне. Спасибо.

// The individual post component
const Post = props => (
    <article className = "post">
        <h2 className = "post-title">{props.title}</h2>
        <hr />
        <p className = "post-content">{props.content}</p>
    </article>
);

// The form component to be written later

class Forms extends React.Component {}

// The posts loop component

class Posts extends React.Component {
    state = {
        posts: null,
        post: {
            title: "",
            content: ""
        }
        // error:false
    };

    componentDidMount() {
        // const posts = this.state.posts;
        axios
            .get("firebaseURL/posts.json")
            .then(response => {
                const updatedPosts = response.data;
                // const updatedPosts = Array.from(response.data).map(post => {
                //  return{
                //      ...post
                //  }
                // });
                this.setState({ posts: updatedPosts });
                console.info(response.data);
                console.info(updatedPosts);
            });
    }
    handleChangeEvent = event => {
        const name = event.target.name;
        const value = event.target.value;
        const { post } = this.state;
        const newPost = {
            ...post,
            [name]: value
        };
        this.setState({ post: newPost });
        console.info(event.target.value);
        console.info(this.state.post.title);
        console.info(name);
    };

    handleSubmit = event => {
        event.preventDefault();
        const post = {
            post: this.state.post
        };
        const posts = this.state.posts;
        axios
            .post("firebaseURL/posts.json", post)
            .then(response => {
                console.info(response);
                this.setState({ post: response.data });
            });
    };

    render() {
        let posts = <p>No posts yet</p>;
        if (this.state.posts) {
            posts = this.state.posts.map(post => {
                return <Post key = {post.id} {...post} />;
            });
        }

        return (
            <React.Fragment>
                <form className = "new-post-form" onSubmit = {this.handleSubmit}>
                    <label>
                        Post title
                        <input
                            className = "title-input"
                            type = "text"
                            name = "title"
                            onChange = {this.handleChangeEvent}
                        />
                    </label>
                    <label>
                        Post content
                        <input
                            className = "content-input"
                            type = "text"
                            name = "content"
                            onChange = {this.handleChangeEvent}
                        />
                    </label>
                    <input className = "submit-button" type = "submit" value = "submit" />
                </form>
            </React.Fragment>
        );
    }
}

class App extends React.Component {
    render() {
        return (
            <React.Fragment>
                <Posts />
            </React.Fragment>
        );
    }
}
// Render method to run the app

ReactDOM.render(<App />, document.getElementById("id"));

А это скриншот моей базы данных firebase: Моя структура базы данных Firebase

Получение ответа и установка состояния - не большая проблема в вашем случае. Хотя ваш ответ представляет собой объект, а не массив, не так сложно им управлять и обновлять состояние. Но проблема в том, что ваша форма БД неоднородна. Есть объект, у которого есть объект сообщения, есть объект, у которого есть объект с идентификатором, у которого есть объект сообщения, есть даже более странный вложенный объект :) Итак, прежде всего, просто разберитесь. Я мало знаю Firebase, но после того, как вы решите эту проблему, получить ответ и управлять им очень легко. Есть методы вроде Object.keys.

devserkan 01.10.2018 01:56

Спасибо. Как я уже упоминал, у моего метода публикации может быть проблема. Как мне отправить данные, чтобы получить эти данные в виде массива, а не вложенных?

azad6026 01.10.2018 02:21

Итак, ваш главный вопрос связан с сохранением в БД Firebase. Вы можете изменить название в соответствии с этим и, возможно, добавить тег firebase.

devserkan 01.10.2018 02:29

Это был не мой главный вопрос. Но я подозревал, что проблема в этом. Я все равно добавил firebase.

azad6026 01.10.2018 04:45
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
4
1 762
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Не эксперт по базам данных, но я считаю, что ваша база данных имеет немного странную структуру и вызовет проблемы только в дальнейшем, особенно когда дело доходит до редактирования / обновления одного сообщения. В идеале он должен иметь структуру массива JSON:

posts: [
  {
    id: "LNO_qS0Y9PjIzGds5PW",
    title: "Example title",
    content: "This is just a test"
  },
  {
    id: "LNOc1vnvA57AB4HkW_i",
    title: "Example title",
    content: "This is just a test"
  },
   ...etc
]

вместо этого он структурирован как объект JSON:

"posts": {
  "LNO_qS0Y9PjIzGds5PW": {
     "post": {
       "title": "Example title",
       "content": "This is just a test"
     }
   },
   "LNOc1vnvA57AB4HkW_i": {
      "post": {
       "title": "Example title",
       "content": "This is just a test"
     }
   },
   ...etc
}

В любом случае, ваш проект должен иметь родительский Postscontainer-component, который контролирует все ваше состояние и выборку данных, а затем передает свои state и класс methods компоненту children. Затем children может соответственно обновить или отобразить родительский state.

ИЛИ

Вы должны разделить ваш Postscontainer-component, чтобы он отображал найденные сообщения или компонент «Сообщения не найдены». И затем пусть ваш компонент Posts Form будет собственным / неразделенным компонентом, единственная функция которого - показать форму и отправить ее в БД.

На ваше усмотрение и то, что, по вашему мнению, соответствует вашим потребностям.


Рабочий пример: https://codesandbox.io/s/4x4kxn9qxw (в приведенном ниже примере есть один container-component, который используется совместно со многими дочерними элементами)

Примечание. Если вы измените posts на пустой массив [], вместо data в функции fetchData()s this.setState(), вы можете отобразить PostForm в маршруте /posts!

пример: .then(({ data }) => this.setState({ isLoading: false, posts: [] }))

index.js

import React from "react";
import { render } from "react-dom";
import App from "./routes";
import "uikit/dist/css/uikit.min.css";
import "./styles.css";

render(<App />, document.getElementById("root"));

маршруты / index.js

import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Home from "../components/Home";
import Header from "../components/Header";
import Posts from "../containers/Posts";

export default () => (
  <BrowserRouter>
    <div>
      <Header />
      <Switch>
        <Route exact path = "/" component = {Home} />
        <Route path = "/posts" component = {Posts} />
        <Route path = "/postsform" component = {Posts} />
      </Switch>
    </div>
  </BrowserRouter>
);

контейнеры / Posts.js

import isEmpty from "lodash/isEmpty";
import React, { Component } from "react";
import axios from "axios";
import PostsForm from "../components/postsForm";
import ServerError from "../components/serverError";
import ShowPosts from "../components/showPosts";
import Spinner from "../components/spinner";

export default class Posts extends Component {
  state = {
    content: "",
    error: "",
    isLoading: true,
    posts: [],
    title: ""
  };

  componentDidUpdate = (prevProps, prevState) => {
    // check if URL has changed from "/posts" to "/postsform" or vice-versa
    if (this.props.location.pathname !== prevProps.location.pathname) {
      // if so, check the location
      this.setState({ isLoading: true }, () => this.checkLocation());
    }
  };

  componentDidMount = () => this.checkLocation();

  checkLocation = () => {
    // if the location is "/posts" ...
    this.props.location.pathname === "/posts"
      ? this.fetchData() // then fetch data
      : this.setState({  // otherwise, clear state
          content: "",
          error: "",
          isLoading: false,
          posts: [],
          title: ""
        });
  };

  // fetches posts from DB and stores it in React state
  fetchData = () => {
    axios
      .get("firebaseURL/posts.json")
      .then(({ data }) => this.setState({ isLoading: false, posts: data }))
      .catch(err => this.setState({ error: err.toString() }));
  };

  // handles postsForm input changes { content: value , title: value }
  handleChangeEvent = e => this.setState({ [e.target.name]: e.target.value });

  // handles postsForm form submission
  handleSubmit = event => {
    event.preventDefault();
    const { content, title } = this.state;

    alert(`Sumbitted values: ${title} - ${content}`);

   /* axios.post("firebaseURL/posts.json", { post: { title, content }})
        .then(({data}) => this.setState({ content: "", posts: data, title: "" }))
        .catch(err => this.setState({ error: err.toString() }))
   */
  };

  // the below simply returns an if/else chain using the ternary operator
  render = () => (
    this.state.isLoading // if isLoading is true...
      ? <Spinner />  // show a spinner
      : this.state.error  // otherwise if there's a server error...
         ? <ServerError {...this.state} />  // show the error
         : isEmpty(this.state.posts) // otherwise, if posts array is still empty..
            ? <PostsForm  // show the postForm
                {...this.state}
                handleChangeEvent = {this.handleChangeEvent}
                handleSubmit = {this.handleSubmit}
              />
            : <ShowPosts {...this.state} /> // otherwise, display found posts!
  );
}

компоненты / postsForm.js

import React from "react";

export default ({ content, handleSubmit, handleChangeEvent, title }) => (
  <form
    style = {{ padding: "0 30px", width: 500 }}
    className = "new-post-form"
    onSubmit = {handleSubmit}
  >
    <label>
      Post title
      <input
        style = {{ marginBottom: 20 }}
        className = "uk-input"
        type = "text"
        name = "title"
        onChange = {handleChangeEvent}
        placeholder = "Enter post title..."
        value = {title}
      />
    </label>
    <label>
      Post content
      <input
        style = {{ marginBottom: 20 }}
        className = "uk-input"
        type = "text"
        name = "content"
        onChange = {handleChangeEvent}
        placeholder = "Enter post..."
        value = {content}
      />
    </label>
    <button 
      disabled = {!title || !content}
      className = "uk-button uk-button-primary" 
      type = "submit"
    >
      Submit
    </button>
  </form>
);

компоненты / showPosts.js

import map from "lodash/map";
import React from "react";

export default ({ posts }) => (
  <div className = "posts">
    {map(posts, ({ post: { content, title } }, key) => (
      <div key = {key} className = "post">
        <h2 className = "post-title">{title}</h2>
        <hr />
        <p className = "post-content">{content}</p>
      </div>
    ))}
  </div>
);

компоненты / serverError.js

import React from "react";

export default ({ err }) => (
  <div style = {{ color: "red", padding: 20 }}>
    <i style = {{ marginRight: 5 }} className = "fas fa-exclamation-circle" /> {err}
  </div>
);

Спасибо за это. В моей функции отправки дескриптора я изменил объект сообщения на это: const post = {title: this.state.post.title, content: this.state.post.content}; И теперь я добавил это в свое обещание: const updatedPosts = Object.values ​​(data); Я получаю такой массив: 0: {content: "проверено", title: "снова проверено"} 1: {content: "test", title: "test"} 2: {content: "test", title: " test "} Как мне получить из этого массив для использования? Я пробовал map и Object.values ​​и Object.keys, но не повезло. Мне нужны только значения, а не ключи.

azad6026 02.10.2018 00:58

Мне нужно это как один массив: [{content: "проверено", title: "проверено снова"}, {content: "test", title: "test"}, {content: "test", title: "test" }] Любые идеи?

azad6026 02.10.2018 01:37

Используйте оператор spread внутри массива [ ]. Так что это будет const updatedPosts = [ ...data ]. В идеале вам нужно, чтобы к каждому отдельному сообщению был привязан уникальный идентификатор. Таким образом, вы можете выполнить обновление по идентификатору И в функции showPostsmap заменить key = {key} на key = {id}!

Matt Carlotta 02.10.2018 01:55

Я сделал это, но я не могу обновить свое состояние здесь, чтобы использовать его внутри showPosts. This.setState ({posts: updatedPosts}); не заполняет сообщения данными и сообщениями, показывающими пустой массив!

azad6026 02.10.2018 11:41

Разветвите mycodeandbox и обновите его кодом, который вы пытаетесь реализовать, а затем опубликуйте ссылку на свой разветвленныйcodeandbox.

Matt Carlotta 02.10.2018 14:31

Спасибо за это, но я не буду этого делать, так как хочу, чтобы мой код был исправлен. По большей части это правильно. Мне просто нужно выяснить, что не так с не обновлением состояния. setState может быть вызван сразу в componentDidMount и должен работать здесь. Мне нужно, чтобы кто-то помог мне в этом. Это базовое приложение. Спасибо.

azad6026 03.10.2018 05:56
Ответ принят как подходящий

Интересно, что то, что я нашел, редко где-либо упоминается. Это весь компонент сообщений:

class Posts extends React.Component {
    state = {
        posts: [],
        post: {
            title: "",
            content: ""
        }
    };

    componentWillMount() {
        const { posts } = this.state;
        axios
            .get("firebaseURL/posts.json")
            .then(response => {
            const data = Object.values(response.data);
            this.setState({ posts : data });
            });
    }
    handleChangeEvent = event => {
        const name = event.target.name;
        const value = event.target.value;
        const { post } = this.state;
        const newPost = {
            ...post,
            [name]: value
        };
        this.setState({ post: newPost });
        console.info(event.target.value);
        console.info(this.state.post.title);
        console.info(name);
    };

    handleSubmit = event => {
        event.preventDefault();
        const {post} = this.state;
        const {posts} = this.state;
        axios
            .post("firebaseURL/posts.json", post)
            .then(response => {
                console.info(response);
              const newPost = response.data;
                this.setState({ post: response.data });
            });
    };

    render() {
        let posts = <p>No posts yet</p>;
        if (this.state.posts) {
            posts = this.state.posts.map(post => {
                return <Post key = {post.id} {...post} />;
            });
        }

        return (
            <React.Fragment>
                {posts}
                <form className = "new-post-form" onSubmit = {this.handleSubmit}>
                    <label>
                        Post title
                        <input
                            className = "title-input"
                            type = "text"
                            name = "title"
                            onChange = {this.handleChangeEvent}
                        />
                    </label>
                    <label>
                        Post content
                        <input
                            className = "content-input"
                            type = "text"
                            name = "content"
                            onChange = {this.handleChangeEvent}
                        />
                    </label>
                    <input className = "submit-button" type = "submit" value = "submit" />
                </form>
            </React.Fragment>
        );
    }
}

На самом деле, когда я впервые читал в этот вопрос, вам не следует полагаться на console.info, чтобы увидеть, были ли обновлены ваши сообщения (или данные вашего ответа). Потому что в componentDidMount () при немедленном обновлении состояния вы не увидите изменения в консоли. Итак, что я сделал, так это отобразил данные, которые я получил из ответа, используя карту поверх сообщений, и он показал мои элементы, поскольку у меня на самом деле был массив, хотя я не мог видеть в консоли. Это мой код для componentDidMount:

axios.get("firebaseURL/posts.json").then(response => {
    const data = Object.values(response.data);
    this.setState({
        posts: data
});

И покажите сообщения:

let posts = <p>No posts yet</p>;
if (this.state.posts) {
    posts = this.state.posts.map(post => {
        return <Post key = {post.id} {...post} />;
    });
}

И он показывает все сообщения, как ожидалось. Забудьте о том, что будьте осторожны при работе с componentDidMound и другими методами жизненного цикла, поскольку вы можете не видеть обновленные данные в консоли внутри них, но вам действительно нужно использовать их в том виде, в котором они есть в ответе. Состояние обновлено, но вы не можете увидеть его внутри этого метода.

У меня была такая же проблема, и Object.values ​​ее решили. Спасибо!

vtomic85 12.03.2019 19:39

Приятно это слышать. Они очень удобны в таких случаях.

azad6026 17.03.2019 09:57

Другие вопросы по теме