Data is appending next to the datatable, not inside.
I am fetching data (array of records) from an API in actions of vuex and returning state (array) from getters to the components where datatables have been used.
import axios from "../../assets/constants";
import router from '../../router'
const state = {
users: []
}
const getters = {
users: state => state.users,
blockedUsers: state => state.blockedUsers,
user: state => state.user
}
const actions = {
async getUsers({ commit }) {
await axios.get(`user`)
.then(res => {
commit('setGetUsers', res.data)
})
.catch(err => console.log(err.response.data.message));
})
},
const mutations = {
setGetUsers: (state, newUsers) => (state.users = newUsers),
}
export default {
state,
getters,
actions,
mutations
}
<script>
import { mapGetters, mapActions } from "vuex";
export default {
methods: {
...mapActions(["getUsers"])
},
computed: mapGetters(["users"]),
created() {
this.getUsers();
$(".zero-configuration").DataTable();
}
};
</script>
Result should be as that data that I am fetching from API must show inside datatable.
As far I understand, issue that has been causing here is that
$(".zero-configuration").DataTable();
this is executing before
this.getUsers()
which shouldn't be correct explanation because I have used await with axios.
Can anyone explain why is this happening?
It turns out when I commit mutation after I get response from axios, it takes time to set the state. Since I am not using promise here, while the state is being mutate,
$(".zero-configuration").DataTable();
takes control from
this.getUsers()
and get executed before it finishes.
I encountered this problem by using promise in getUsers action
getUsers({ commit }) {
return new Promise(async (resolve) => {
await axios.get(`user`)
.then(async res => {
await commit('setGetUsers', res.data)
resolve()
})
.catch(err => console.log(err.response.data.message));
})
},
Now it works like a charm!
Related
Beginner here.
Trying to fetch some data from a server and display it in my react component once its fetched.
However, I am having trouble integrating the async function into my react component.
import React, { useState } from "react";
import { request } from "graphql-request";
async function fetchData() {
const endpoint = "https://localhost:3090/graphql"
const query = `
query getItems($id: ID) {
item(id: $id) {
title
}
}
`;
const variables = {
id: "123123123"
};
const data = await request(endpoint, query, variables);
// console.log(JSON.stringify(data, undefined, 2));
return data;
}
const TestingGraphQL = () => {
const data = fetchData().catch((error) => console.error(error));
return (
<div>
{data.item.title}
</div>
);
};
export default TestingGraphQL;
I'd like to simply show a spinner or something while waiting, but I tried this & it seems because a promise is returned I cannot do this.
Here you would need to use the useEffect hook to call the API.
The data returned from the API, I am storing here in a state, as well as a loading state to indicate when the call is being made.
Follow along the comments added in between the code below -
CODE
import React, { useState, useEffect } from "react"; // importing useEffect here
import Layout from "#layouts/default";
import ContentContainer from "#components/ContentContainer";
import { request } from "graphql-request";
async function fetchData() {
const endpoint = "https://localhost:3090/graphql"
const query = `
query getItems($id: ID) {
item(id: $id) {
title
}
}
`;
const variables = {
id: "123123123"
};
const data = await request(endpoint, query, variables);
// console.log(JSON.stringify(data, undefined, 2));
return data;
}
const TestingGraphQL = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
// useEffect with an empty dependency array works the same way as componentDidMount
useEffect(async () => {
try {
// set loading to true before calling API
setLoading(true);
const data = await fetchData();
setData(data);
// switch loading to false after fetch is complete
setLoading(false);
} catch (error) {
// add error handling here
setLoading(false);
console.log(error);
}
}, []);
// return a Spinner when loading is true
if(loading) return (
<span>Loading</span>
);
// data will be null when fetch call fails
if (!data) return (
<span>Data not available</span>
);
// when data is available, title is shown
return (
<Layout>
{data.item.title}
</Layout>
);
};
since fetchData() returns a promise you need to handle it in TestingGraphQL. I recommend onComponentMount do your data call. Setting the data retrieved into the state var, for react to keep track of and re-rendering when your data call is finished.
I added a loading state var. If loading is true, then it shows 'loading' otherwise it shows the data. You can go about changing those to components later to suit your needs.
See the example below, switched from hooks to a class, but you should be able to make it work! :)
class TestingGraphQL extends Component {
constructor() {
super();
this.state = { data: {}, loading: true};
}
//when the component is added to the screen. fetch data
componentDidMount() {
fetchData()
.then(json => { this.setState({ data: json, loading: false }) })
.catch(error => console.error(error));
}
render() {
return (
{this.state.loading ? <div>Loading Spinner here</div> : <div>{this.state.data.item.title}</div>}
);
}
};
I am learning react-redux async actions with redux-thunk, I would like to fetch data from API (my local database), Unfortunately fetching data with redux-thunk middleware data is not fetched but without thunk middleware data is fetched.
So here are action creators with thunk middleware, which is not working
// retriev comments
export const fetchComments= () =>{
return dispatch =>{
dispatch(fetchCommentsRequest);
axios.get('/api/v1/todo')
.then(response =>{
const comments =response.data;
dispatch(fetchCommentsSucces(comments))
})
.catch(error =>{
const erroMsg =errors.messages
dispatch(fetchCommentsFailure(error))
})
}
}
And here is console log result :
Here is a component where I am calling the function to fetch data from API,
import React, {useEffect}from 'react'
import { fetchComments} from '../store/actions'
import { connect } from "react-redux";
function Dashboard(userComments) {
useEffect(() =>{
fetchComments();
}, [])
return (
<div>
<p>Fetching data</p>
</div>
)
}
const mapStateToProps = state => {
console.log("I am state", state);
return {
isAuthenticated: state.Auth.isAuthenticated,
user: state.Auth.user,
userComments: state.comments
};
};
const mapDispatchToProps = dispatch => {
return {
fetchComments: () => dispatch(fetchComments()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
The whole store can be found here: store
Can someone tells me why data is not fetched?
There is an issue with how fetchComments method is called inside the <Dashboard> component.
Once a React component is connected to a Redux store, the data from the store (mapStateToProps) and the functions it can use to dispatch actions to the store (mapDispatchToProps) are passed to that component as an object.
The <Dashboard> component receives this props object that can be accessed inside it like:
function Dashboard(props) {
useEffect(() =>{
props.fetchComments();
}, [])
return (
<div>
<p>Fetching data</p>
</div>
)
}
or using destructuring:
function Dashboard({ isAuthenticated, user, userComments, fetchComments }) {
useEffect(() =>{
fetchComments();
}, [])
return (
<div>
<p>Fetching data</p>
</div>
)
}
In your thunk, dispatch the action properly i.e. call the fetchCommentsRequest function (you are providing reference)
export const fetchComments= () =>{
return dispatch =>{
dispatch(fetchCommentsRequest()); //<-----call the fuction
axios.get('/api/v1/todo')
.then(response =>{
const comments =response.data;
dispatch(fetchCommentsSucces(comments))
})
.catch(error =>{
const erroMsg =errors.messages
dispatch(fetchCommentsFailure(error))
})
}
}
In your repo, the fetchCommentsSucces needs to take an argument.
export function fetchCommentsSucces(comments){ //<----pass argument i.e comments
console.log('success')
return{
type: ActionTypes.FETCH_COMMENTS_SUCCESS,
payload: comments //<----provide correct payload
}
}
I'm learning React and Redux. And I may have a really basic question.
I want to get a single story from my backend using the Redux function mapStateToProps (#1). So I wrote the function getSingleStory which takes the id as argument and returns the story data (#2). When I log the response data of the getSingleStory in the console, it shows me the correct story fetched from the backend (#3):
However, if the console logs the story array in my component (#4), it outputs all stories from my database, not just the single story I wanted to fetch (see picture). If I want to display 'Story.title', in my render function of course it does not work.
If someone could explain to me why in the response data the single story is included and in the const story = this.props.story; all stories suddenly appear, that would help me a lot.
export class StoryDetails extends Component {
componentDidMount() { // #2
this.props.getSingleStory(this.props.match.params.id);
}
render() {
const story = this.props.story;
console.log (story); // #4
return (
<div>
<h2>{story.title}</h2>
</div>
);
}
}
const mapStateToProps = state => ({story: state.story}); //#1
export default connect(
mapStateToProps,
{ getSingleStory, deleteStory}
)(StoryDetails);
Action
// GET SINGLE STORY
export const getSingleStory = id => (dispatch, getState) => {
return new Promise((resolve, reject) => {
axios.get( apiBase + `/story/${id}/`, tokenConfig(getState))
.then(res => {
dispatch({
type: GET_SINGLE_STORY,
story: res.data
}, console.log (res.data)); //#3
resolve(res);
})
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status));
reject(err);
});
});
};
Reducer
import { GET_SINGLE_STORY } from "../actions/types.js";
export default function (state = {}, action) {
switch (action.type) {
case GET_SINGLE_STORY:
return action.story;
default:
return state;
}
};
Many Thanks in advance!
I am trying to print out user related items only.
So i am try to get items by requesting data to user id /api/items/:userid
I am using redux store
my server side code is like this
router.get("/:userid",(req, res) => {
// Item.find({ "owner.ownerName": `${req.params.userid}`})
Item.find({ "owner.id": `${req.params.userid}`})
.sort({
date: -1,
})
.then((items) => res.json(items));
console.log(req.user)
});
The problem is my front end request.
I don't know how to get user id inside ITEMACTION.
import {
GET_ITEMS,
ADD_ITEM,
DELETE_ITEM,
ITEMS_LOADING,
UPDATE_ITEM,
SUBSTRACT_ITEM,
} from "../actions/types";
import { tokenConfig } from "../actions/authActions";
import { returnErrors } from "../actions/errorActions";
import Axios from "axios";
export const getItems = () => (dispatch) => {
// will hit reducer
dispatch(setItemsLoading());
Axios.get("/api/items/")
.then((res) =>
dispatch({
type: GET_ITEMS,
payload: res.data,
})
)
.catch((err) => {
dispatch(returnErrors(err.response.data, err.response.status));
});
};
I actually tried to get user id from the redux store.
import store from '../store';
and inside getItems
store.getState().auth.user._id
the problem is that when i console.log in getItems the user id is always return null except first time after login. But when i look in redux dev tool. The user id is available
how can i get the userid
Hey you can get the getState as a second argument in the inner function along with the dispatch, using that you can access the updated state in an action.
Fixed Code:
export const getItems = () => (dispatch, getState) => {
// will hit reducer
const userId = getState().auth.user._id;
console.log(userId) // should output the updated data
dispatch(setItemsLoading());
Axios.get("/api/items/")
.then((res) =>
dispatch({
type: GET_ITEMS,
payload: res.data,
})
)
.catch((err) => {
dispatch(returnErrors(err.response.data, err.response.status));
});
};
store.getState doesn't return updated state, in order to get the updated state using store.getState() you need to subscribe to the state change.
const unsubscribe = store.subscribe(() => {
// logs the state data everytime an action is dispatched.
console.log("from listener: ", store.getState());
})
Details here
I'm trying to call an action in my vue from my store.
This is my file aliments.js in my store:
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';
Vue.use(Vuex, axios);
export const state = () => ({
aliments: {},
})
export const mutations = () => ({
SET_ALIMENTS(state, aliments) {
state.aliments = aliments
}
})
export const actions = () => ({
async getListAliments(commit) {
await Vue.axios.get(`http://localhost:3080/aliments`).then((response) => {
console.log(response);
commit('SET_ALIMENTS', response);
}).catch(error => {
throw new Error(`${error}`);
})
// const data = await this.$axios.get(`http://localhost:3080/aliments`)
// commit('setUser', user)
// state.user = data;
// return state.user;
}
})
export const getters = () => ({
aliments (state) {
return state.aliments
}
})
I want to diplay a list of aliments in my vue with :
{{ this.$store.state.aliments }}
I call my action like this :
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
computed: {
...mapGetters(['loggedInUser', 'aliments']),
...mapActions(['getListAliments']),
getListAliments() {
return this.$state.aliments
}
}
}
</script>
I don't understand where is my mistake :/
NB: I also tried with a onclick method on a button with a dispatch('aliments/getListAliments')... but doesn't work...
The problem is that you're mapping your actions in the "computed" section of the component, you should map it in the "methods" section !
Hi and Welcome to StackOverflow
to quickly answer to your question, you would call an action as:
this.$store.dispatch('<NAME_OF_ACTION>', payload)
or though a mapActions as
...mapActions(['getListAliments']), // and you call `this.getListAliments(payload)`
or yet
...mapActions({
the_name_you_prefer: 'getListAliments' // and you call `this.the_name_you_prefer(payload)`
}),
for getters, it's the same process, as you already have 2 definitions ['loggedInUser', 'aliments'] you simply call the getter like if it was a computed value <pre>{{ aliments }}</pre>
or when we need to do a bit more (like filtering)
getListAliments() {
return this.$store.getters['aliments']
}
But I can see your store is as we call, one-to-rule-them-all, and because you are using Nuxt, you can actually leverage the module store very easy
as your application grows, you will start store everything in just one store file (the ~/store/index.js file), but you can easily have different stores and instead of what you wrote in index.js it can be easier if you had a file called, taken your example
~/store/food.js with
import axios from 'axios'
export const state = () => ({
aliments: {},
})
export const getters = {
aliments (state) {
return state.aliments
}
}
export const mutations = {
SET_ALIMENTS(state, aliments) {
state.aliments = aliments
}
}
export const actions = {
async getListAliments(commit) {
await axios.get('http://localhost:3080/aliments')
.then((response) => {
console.log(response);
commit('SET_ALIMENTS', response.data);
}).catch(error => {
throw new Error(`${error}`);
})
}
}
BTW, remember that, if you're using Nuxt serverMiddleware, this line
axios.get('http://localhost:3080/aliments')...
would simply be
axios.get('/aliments')...
and to call this store, all you need is to prefix with the filename, like:
...mapActions(['food/getListAliments'])
// or
...mapActions({ getListAliments: 'food/getListAliments' })
// or
this.$store.commit('food/getListAliments', payload)
another naming that could help you along the way:
on your action getListAliments you're actually fetching data from the server, I would change the name to fetchAliments
on your getter aliments you're actually returning the list, I would name it getAllAliments
have fun, Nuxt is amazing and you have a great community on Discord as well for the small things :o)
EDIT
also remember that actions are set in methods
so you can do:
...
export default {
methods: {
...mapActions(['getListAliments]),
},
created() {
this.getListAliments()
}
}
and in your Store action, please make sure you write
async getListAliments({ commit }) { ... }
with curly braces as that's a deconstruction of the property passed
async getListAliments(context) {
...
context.commit(...)
}