Uncaught (in promise) ReferenceError: state is not defined
Появляется такая ошибка:
overflow.com/image.jpg)
Использую vuex, методом проб понял, что приложение ломается из-за состояния isPostsLoading: false в компоненте postModule.js . Это состояние мы передаем в компонент PostsPageWithStore.vue , если закомментировать это состояние, то всё работает. Как можно это пофиксить?
import { createStore } from 'vuex'
import { postModule } from './postModule'
export default createStore({
/* один модуль представляет из себя какой-то один изолированный кусочек состояния со своими getters, mutations, actions */
modules: {
/* post - название модуля */
post: postModule,
},
})
<!-- компонент PostModule.js -->
import axios from 'axios'
export const postModule = {
/* В поле state мы описываем данные, которые будут в нашем приложении */
state: () => ({
posts: [],
isPostsLoading: false,
selectedSort: '',
searchQuery: '',
page: 1,
limit: 10,
totalPages: 0,
sortOptions: [
{ value: 'title', name: 'По названию' },
{ value: 'body', name: 'По содержанию' },
],
}),
/* это некоторые computed св-ва анаогичные тем, что мы создавали в компонентах, это своего рода кэщируемые вычисляемые значения. Изменять состояния мы не можем, но можем изменять с помощью мутаций */
getters: {
sortedPosts(state) {
return [...state.posts].sort((post1, post2) =>
post1[state.selectedSort]?.localeCompare(post2[state.selectedSort])
)
},
/* в новом computed свойстве мы используем старое */
sortedAndSearchedPosts(state, getter) {
return getter.sortedPosts.filter((post) =>
post.title.toLowerCase().includes(state.searchQuery.toLowerCase())
)
},
},
/* Мтации представляют из себя функции, внутри которых мы (меняем состояние) меняем значение какого-то поля в состоянии. Для вызова мутаций используется функция commit */
mutations: {
setPosts(state, posts) {
state.posts = posts
},
// setIsPostsLoading(state, bool) {
// state.isPostsLoading = bool
// },
setSelectedSort(state, selectedSort) {
state.selectedSort = selectedSort
},
setSearchQuery(state, query) {
state.searchQuery = query
},
setPage(state, page) {
state.page = page
},
setTotalPages(state, totalPages) {
state.totalPages = totalPages
},
},
/* Функции, которые внутри себя используют мутации, т.е. напрямую из actions менять состяние не рекомендуется, но мы можем использовать внутр экшенров - мутации. В свою очередь внутри экшенов мы делаем какие-то сайды эффектами (например получаем данные с сервера) */
actions: {
/* commit - нужен для вызова мутаций, dispatch - для вызова других actions */
async fetchPosts({ state, commit }) {
try {
commit('setIsPostsLoading', true)
const res = await axios.get(
`https://jsonplaceholder.typicode.com/posts`,
{
params: {
_page: state.page,
_limit: state.limit,
},
}
)
/* Мы меняем totalPages, поэтому мы совершаем соответствующую мутацию. commit первым параметром принимает название, а вторым - результат вычислений */
commit(
'setTotalPages',
Math.ceil(res.headers['x-total-count'] / state.limit)
)
commit('setPosts', res.data)
} catch (error) {
console.log(error)
} finally {
commit('setIsPostsLoading', false)
}
},
/* эта функция вызывается, когда пользователь доскроллил до конца страницы */
async loadMorePosts({ state, commit }) {
try {
commit('setPage', state.page + 1)
const res = await axios.get(
`https://jsonplaceholder.typicode.com/posts`,
{
params: {
_page: state.page,
_limit: state.limit,
},
}
)
commit(
'setTotalPages',
Math.ceil(res.headers['x-total-count'] / state.limit)
)
/* В этом случае мы посты не перезаписываем, а добавляем в конец массива */
commit('setPosts', [...state.posts, ...res.data])
} catch (error) {
console.log(error)
}
},
},
namespaced: true,
}
<!-- компонент PostsPageWithStore.vue -->
<template>
<div>
<h1>Страница с постами</h1>
<!-- <my-input v-focus v-model="searchQuery" placeholder="Поиск..." /> -->
<div class="app__btns">
<my-button @click="showDialog">Cоздать пост</my-button>
<!-- <my-select v-model="selectedSort" :options="sortOptions" /> -->
</div>
<my-dialog v-model:show="dialogVisible">
<post-form @create="createPost" />
</my-dialog>
<post-list
:posts="sortedAndSearchedPosts"
@remove="removePost"
v-if="!isPostsLoading"
/>
<div v-else class="loader">Загрузка...</div>
<div v-intersection="loadMorePosts" class="observer"></div>
</div>
</template>
<script>
import PostForm from '@/components/PostForm.vue'
import PostList from '@/components/PostList.vue'
/* Чтобы не прописывать такой долгий путь, придумали специальные функции mapState, mapActions и тд. :posts="$store.getters.post/sortedAndSearchedPosts" */
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'
export default {
/* поле components - для регистации комопнентов */
components: { PostList, PostForm },
data() {
return {
dialogVisible: false,
}
},
/* Работа с mutations, actions ведется в поле methods */
methods: {
...mapMutations({
setPage: 'post/setPage',
}),
...mapActions({
loadMorePosts: 'post/loadMorePosts',
fetchPosts: 'post/fetchPosts',
}),
/* мы подписались на это событие и принимаем post из компонента PostForm */
createPost(post) {
this.posts.push(post)
this.dialogVisible = false
},
removePost(post) {
this.posts = this.posts.filter((p) => p.id !== post.id)
},
showDialog() {
this.dialogVisible = true
},
},
/* хук для того, чтобы сделать запрос на сервер после маунта */
mounted() {
this.fetchPosts()
},
computed: {
...mapState({
posts: () => state.post.posts,
isPostsLoading: () => state.post.isPostsLoading,
selectedSort: () => state.post.selectedSort,
searchQuery: () => state.post.searchQuery,
page: () => state.post.page,
limit: () => state.post.limit,
totalPages: () => state.post.totalPages,
sortOptions: () => state.post.sortOptions,
}),
...mapGetters({
sortedPosts: 'post/sortedPosts',
sortedAndSearchedPosts: 'post/sortedAndSearchedPosts',
}),
},
watch: {},
}
</script>
<style>
.app__btns {
margin: 15px 0;
display: flex;
justify-content: space-between;
}
.page__wrapper {
display: flex;
margin-top: 15px;
}
.page {
border: 1px solid black;
padding: 10px;
}
.current-page {
border: 3px solid teal;
}
.observer {
height: 30px;
background: green;
}
</style>