Интересно, может ли кто-нибудь мне помочь. Я прочитал много ответов 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. Вы можете изменить название в соответствии с этим и, возможно, добавить тег firebase.
Это был не мой главный вопрос. Но я подозревал, что проблема в этом. Я все равно добавил firebase.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Не эксперт по базам данных, но я считаю, что ваша база данных имеет немного странную структуру и вызовет проблемы только в дальнейшем, особенно когда дело доходит до редактирования / обновления одного сообщения. В идеале он должен иметь структуру массива 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, но не повезло. Мне нужны только значения, а не ключи.
Мне нужно это как один массив: [{content: "проверено", title: "проверено снова"}, {content: "test", title: "test"}, {content: "test", title: "test" }] Любые идеи?
Используйте оператор spread внутри массива [ ]. Так что это будет const updatedPosts = [ ...data ]. В идеале вам нужно, чтобы к каждому отдельному сообщению был привязан уникальный идентификатор. Таким образом, вы можете выполнить обновление по идентификатору И в функции showPostsmap заменить key = {key} на key = {id}!
Я сделал это, но я не могу обновить свое состояние здесь, чтобы использовать его внутри showPosts. This.setState ({posts: updatedPosts}); не заполняет сообщения данными и сообщениями, показывающими пустой массив!
Разветвите mycodeandbox и обновите его кодом, который вы пытаетесь реализовать, а затем опубликуйте ссылку на свой разветвленныйcodeandbox.
Спасибо за это, но я не буду этого делать, так как хочу, чтобы мой код был исправлен. По большей части это правильно. Мне просто нужно выяснить, что не так с не обновлением состояния. setState может быть вызван сразу в componentDidMount и должен работать здесь. Мне нужно, чтобы кто-то помог мне в этом. Это базовое приложение. Спасибо.
Интересно, что то, что я нашел, редко где-либо упоминается. Это весь компонент сообщений:
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 ее решили. Спасибо!
Приятно это слышать. Они очень удобны в таких случаях.
Получение ответа и установка состояния - не большая проблема в вашем случае. Хотя ваш ответ представляет собой объект, а не массив, не так сложно им управлять и обновлять состояние. Но проблема в том, что ваша форма БД неоднородна. Есть объект, у которого есть объект сообщения, есть объект, у которого есть объект с идентификатором, у которого есть объект сообщения, есть даже более странный вложенный объект :) Итак, прежде всего, просто разберитесь. Я мало знаю Firebase, но после того, как вы решите эту проблему, получить ответ и управлять им очень легко. Есть методы вроде
Object.keys.