【问题标题】:Axios get method response in React cannot be displayed getting data from firebase as an array in my blog applicationReact 中的 Axios 获取方法响应无法在我的博客应用程序中显示从 firebase 获取数据作为数组
【发布时间】:2019-03-06 01:59:00
【问题描述】:

我想知道是否有人可以帮助我。我已经阅读了很多 StackOverflow 的答案以及其他很棒的文章,例如 this one,但我还无法实现答案。

我在 React 中有一个简单的博客应用程序。我有一个表格来提交数据,我也有单独的帖子和帖子组件。我实际上可以将数据发送到我的 firebase 数据库。我也得到了 GET 方法的响应,但我无法显示我需要的响应。我需要一组帖子,每个帖子都有一个标题和内容,以便我可以将其数据发送到我的帖子组件。但是我总是得到一个错误,比如(地图不能用于响应),我实际上无法从我的数据库中获取一个数组。我什至想知道我是否以正确的格式发送数据。请检查我下面的代码并帮助我。谢谢。

// 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.log(response.data);
                console.log(updatedPosts);
            });
    }
    handleChange = 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.log(event.target.value);
        console.log(this.state.post.title);
        console.log(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.log(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.handleChange}
                        />
                    </label>
                    <label>
                        Post content
                        <input
                            className="content-input"
                            type="text"
                            name="content"
                            onChange={this.handleChange}
                        />
                    </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 数据库的屏幕截图: My Firebase database structure

【问题讨论】:

  • 获取响应和设置状态在您的情况下不是一个大问题。尽管您的响应是一个对象而不是一个数组,但操作它并更新状态并不难。但是,这里的问题是您的 DB 形状不是同质的。有一个对象有一个 post 对象,有一个对象的 id 有一个 post 对象,甚至还有一个更奇怪的嵌套对象 :) 所以,首先,弄清楚这一点。我不太了解 Firebase,但是在您解决了这个问题之后,得到响应并对其进行操作非常容易。有Object.keys之类的方法。
  • 谢谢。正如我提到的,我的 post 方法可能有问题。我应该如何发送数据以将该数据作为数组而不是嵌套?
  • 因此,您的主要问题与保存到 Firebase 数据库有关。您可以据此更改标题,也可以添加firebase 标签。
  • 这不是我的主要问题。但我怀疑这是否是问题所在。我还是添加了 firebase。

标签: javascript reactjs firebase axios


【解决方案1】:

有趣的是,我发现的东西在它周围的任何地方都很少被提及。 这是整个 Posts 组件:

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 });
            });
    }
    handleChange = 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.log(event.target.value);
        console.log(this.state.post.title);
        console.log(name);
    };

    handleSubmit = event => {
        event.preventDefault();
        const {post} = this.state;
        const {posts} = this.state;
        axios
            .post("firebaseURL/posts.json", post)
            .then(response => {
                console.log(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.handleChange}
                        />
                    </label>
                    <label>
                        Post content
                        <input
                            className="content-input"
                            type="text"
                            name="content"
                            onChange={this.handleChange}
                        />
                    </label>
                    <input className="submit-button" type="submit" value="submit" />
                </form>
            </React.Fragment>
        );
    }
}

实际上,正如我第一次在this question 中看到的那样,您不应该依赖console.log 来查看您的帖子(或您的响应数据)是否已更新。因为在 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 解决了它。谢谢!
  • 很高兴听到这个消息。它们在这些情况下非常方便。
【解决方案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
}

无论如何,您的项目应该有一个父 Posts container-component 来控制您的所有状态和数据获取,然后它将其 state 和类 methods 传递给组件 children。然后children 可以相应地更新或显示父级的state

您应该将您的Postscontainer-component 分开,以便它显示找到的帖子或“未找到帖子”组件。然后,让您的Posts Form 组件成为它自己的/非共享组件,其唯一功能是显示表单并将其提交到数据库。

取决于您以及您认为适合您的需求。


工作示例:https://codesandbox.io/s/4x4kxn9qxw(下面的示例有一个 container-component 与许多孩子共享)

注意:如果将posts 更改为空数组[],而不是fetchData()s this.setState() 函数中的data,则可以在PostForm 路由下显示PostForm

例如:.then(({ data }) =&gt; 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"));

routes/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>
);

containers/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 }
  handleChange = 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}
                handleChange={this.handleChange}
                handleSubmit={this.handleSubmit}
              />
            : <ShowPosts {...this.state} /> // otherwise, display found posts!
  );
}

components/postsForm.js

import React from "react";

export default ({ content, handleSubmit, handleChange, 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={handleChange}
        placeholder="Enter post title..."
        value={title}
      />
    </label>
    <label>
      Post content
      <input
        style={{ marginBottom: 20 }}
        className="uk-input"
        type="text"
        name="content"
        onChange={handleChange}
        placeholder="Enter post..."
        value={content}
      />
    </label>
    <button 
      disabled={!title || !content}
      className="uk-button uk-button-primary" 
      type="submit"
    >
      Submit
    </button>
  </form>
);

components/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>
);

components/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>
);

【讨论】:

  • 谢谢。在我的句柄提交函数中,我已将 post 对象更改为: const post = { title: this.state.post.title, content: this.state.post.content };现在我在我的承诺中添加了这个: const updatedPosts = Object.values(data);我得到一个这样的数组: 0: {content: "tested", title: "tested again"} 1: {content: "test", title: "test"} 2: {content: "test", title: " test"} 我如何从中得到一个数组来使用?我试过 map 和 Object.values 和 Object.keys 但没有运气。我只想要值而不是键。
  • 我需要它作为一个单一的数组: [{content: "tested", title: "tested again"} ,{content: "test", title: "test"} , {content: " test", title: "test"}] 有什么想法吗?
  • 在数组[ ] 中使用spread 运算符。所以应该是const updatedPosts = [ ...data ]。理想情况下,您需要为每个单独的帖子附加一个唯一的 ID。这样,您可以通过 id AND 在showPostsmap 函数中进行更新,将key={key} 替换为key={id}
  • 我这样做了,但我无法在此处更新我的状态,以便我可以在 showPosts 中使用它。 this.setState({ posts : updatedPosts });没有用数据和显示空数组的帖子填充帖子!
  • 分叉我的代码框并使用您尝试实现的代码对其进行更新,然后将链接发布到您的分叉代码框。
猜你喜欢
  • 2021-01-31
  • 2021-07-27
  • 1970-01-01
  • 2021-08-17
  • 2013-09-25
  • 2021-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多