So I'm learning redux currently and I'm making an app that displays a list of articles. But I can't figured out why my data from my back end isn't showing up. I'm not sure where my error is that preventing my data from my backend from showing up? I know it not the setup of redux because I did simpler app to see if that was the problem and it wasn't so it has to do more with the action, reducers , and component. I would like to go farther eventually when there is more data in the database where it provides a link so it goes to another page that shows all the information about that article.
data from my node backend
[{"_id":"58c71df9f7e4e47f1fe17eeb","article":"words words","author":"Jason","date":"1/2/2014","title":"my article","__v":0}]
fashionActions.js
import axios from "axios";
export function fetchFashionArticle() {
return function(dispatch) {
axios.get("http://localhost:3000/api/fashion")
.then((response) => {
dispatch({type: "FETCH_FASHIONARTICLES_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_FASHIONARTICLES_REJECTED", payload: err})
})
}
}
export function addFashionArticle(_id, title,date, author, article) {
return {
type: "ADD_FASHIONARTICLE",
payload: {
_id,
title,
date,
author,
article,
},
}
}
export function updateFashionArticle(_id, title,date, author, article) {
return {
type: "UPDATE_FASHIONARTICLE",
payload: {
_id,
title,
date,
author,
article,
},
}
}
export function deleteFashionArticle(id) {
return {type: 'DELETE_FASHIONARTICLE', payload: id}
}
FashionArticle.js
import React from "react";
import { connect } from "react-redux";
import {fetchFashionArticle} from "../actions/fashionActions";
#connect((store) => {
return {
fashionarticles:store.fashionarticles.fashionarticles,
};
})
export default class FashionArticle extends React.component {
fetchFashionArticle() {
this.props.dispatch(fetchFashionArticle())
}
render() {
const { fashionarticles } = this.props;
if(!fashionarticles.length) {
return <button onClick={this.fetchFashionArticles.bind(this)}>Load articles</button>
}
const mappedArticles = fashionarticles.map(fashionarticle => <li>{fashionarticle}</li>)
return(
<div>
<h1>Fashion Article</h1>
<h2>{fashionarticles.title}</h2>
</div>
)
}
}
fashionArticleReducers.js
export default function reducer(state={
fashionarticles: [],
fetching: false,
fetched: false,
error: null,
}, action) {
switch (action.type) {
case "FETCH_FASHIONARTICLES": {
return {...state, fetching: true}
}
case "FETCH_FASHIONARTICLES_REJECTED": {
return {...state, fetching: false, error: action.payload}
}
case "FETCH_FASHIONARTICLES_FULFILLED": {
return {
...state,
fetching: false,
fetched: true,
fashionarticles: action.payload,
}
}
case "ADD_FASHIONARTICLE": {
return {
...state,
fashionarticles: [...state.fashionarticles, action.payload],
}
}
case "UPDATE_FASHIONARTICLE": {
const { _id, title,date,author,article } = action.payload
const newFashionArticles = [...state.fashionarticles]
const fashionarticleToUpdate = newFashionArticles.findIndex(fashionarticle => fashionarticle.id === id)
newFashionArticles[fashionarticleToUpdate] = action.payload;
return {
...state,
fashionarticles: newFashionArticles,
}
}
case "DELETE_FASHIONARTICLE": {
return {
...state,
fashionarticles: state.fashionarticles.filter(fashionarticle => fashionarticle.id !== action.payload),
}
}
}
return state
}
index.js
import { combineReducers } from 'redux';
import user from './testReducers'
import fashionarticles from './fashionArticleReducers';
export default combineReducers({
user,
fashionarticles,
})
You're sending the payload with the axios response as type FETCH_FASHIONARTICLES_DONE but your reducer is listening for FETCH_FASHIONARTICLES_FULFILLED
Related
I am trying to build an app based on https://github.com/orbitdb/orbit-db-control-center/ which works as intended but when I ported it to my application there are some issues.
Within a useEffect I am calling dispatch which is not changing the application's state. The dispatches are within async functions which are being called and behave as intended but the dispatches aren't updating the UI. I am using parcel to help hot-reload during dev. On page load the dispatches aren't being called but when the page is updated (not-refreshed) using parcel hot-reload the dispatches work as intended.
Systems.js
const [appState, dispatch] = useStateValue()
React.useEffect(() => {
dispatch({ type: actions.PROGRAMS.SET_PROGRAMS_LOADING, loading: true })
initIPFS().then(async (ipfs) => {
dispatch({ type: actions.SYSTEMS.SET_IPFS, ipfsStatus: 'Started' })
initOrbitDB(ipfs).then(async () => {
dispatch({ type: actions.SYSTEMS.SET_ORBITDB, orbitdbStatus: 'Started' })
const programs = await getAllDatabases(pid)
dispatch({ type: actions.PROGRAMS.SET_PROGRAMS, programs: programs.reverse() })
dispatch({ type: actions.PROGRAMS.SET_PROGRAMS_LOADING, loading: false })
})
})
}, [dispatch])
App.js
import React from 'react'
import {Outlet} from 'react-router-dom'
import { actions, loadingState,
StateProvider
} from './state'
import {Systems} from './components/Systems'
import {Header} from './components/Header'
import './index.css'
export function DBView () {
const initialState = {
user: null,
loginDialogOpen: false,
createDBDialogOpen: false,
addDBDialogOpen: false,
programs: [],
program: false,
db: null,
entries: [],
orbitdbStatus: 'Starting',
ipfsStatus: 'Starting',
loading: {
programs: false
}
}
const reducer = (state, action) => {
console.log(action)
switch (action.type) {
case actions.SYSTEMS.SET_ORBITDB:
return {
...state,
orbitdbStatus: action.orbitdbStatus
}
case actions.SYSTEMS.SET_IPFS:
return {
...state,
ipfsStatus: action.ipfsStatus
}
case actions.PROGRAMS.SET_PROGRAM:
return {
...state,
program: action.program
}
case actions.PROGRAMS.SET_PROGRAM_LOADING:
return {
...state,
program: loadingState
}
case actions.PROGRAMS.SET_PROGRAMS:
return {
...state,
programs: action.programs
}
case actions.DB.SET_DB:
return {
...state,
db: action.db,
entries: action.entries,
}
case actions.DB.OPEN_CREATEDB_DIALOG:
return {
...state,
createDBDialogOpen: true
}
case actions.DB.CLOSE_CREATEDB_DIALOG:
return {
...state,
createDBDialogOpen: false
}
case actions.DB.OPEN_ADDDB_DIALOG:
return {
...state,
addDBDialogOpen: true
}
case actions.DB.CLOSE_ADDDB_DIALOG:
return {
...state,
addDBDialogOpen: false
}
case actions.PROGRAMS.SET_PROGRAMS_LOADING:
return {
...state,
loading: { ...state.loading, programs: action.loading }
}
default:
return state
}
}
return (
<StateProvider initialState={initialState} reducer={reducer}>
<Header />
<Systems />
<Outlet />
</StateProvider>
)
}
state/index.js
import React, { createContext, useReducer, useContext } from 'react'
export const StateContext = createContext()
export const StateProvider = ({ reducer, initialState, children }) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
)
export const useStateValue = () => useContext(StateContext)
export const actions = {
DB: {
OPEN_CREATEDB_DIALOG: 'OPEN_CREATEDB_DIALOG',
CLOSE_CREATEDB_DIALOG: 'CLOSE_CREATEDB_DIALOG',
OPEN_ADDDB_DIALOG: 'OPEN_ADDDB_DIALOG',
CLOSE_ADDDB_DIALOG: 'CLOSE_ADDDB_DIALOG',
SET_DB: 'SET_DB'
},
SYSTEMS: {
SET_IPFS: 'SET_IPFS',
SET_ORBITDB: 'SET_ORBITDB'
},
PROGRAMS: {
SET_PROGRAMS: 'SET_PROGRAMS',
SET_PROGRAMS_LOADING: 'SET_PROGRAMS_LOADING',
SET_PROGRAM: 'SET_PROGRAM',
SET_PROGRAM_LOADING: 'SET_PROGRAM_LOADING'
}
}
export const loadingState = 'loading'
I have a mern application using redux for state management.
For some reason when I try to map through it, it tells me it's not a function.
It is weird because when I see my props through the console, it shows me it's an array and react knows that I have data in my state. And it also shows the data in my redux dev tools. But when I try to render it gives me that error. Also when i do this.props.products.products it tells me cannot read property of Null.
Here's the github repo
https://github.com/bryanb213/seller
Can anyone explain why
stuff.jsx
import React, { Component } from 'react'
import './stuff.stle.css'
import { getProducts } from '../redux/actions/productActions';
import { connect } from 'react-redux';
class Stuff extends Component {
componentDidMount() {
this.props.getProducts();
}
render() {
console.log('Products from props', this.props)
if (this.props.loading === true) {
return (
<div>Loading...</div >
)
} else {
return(
<div>
{ this.props.products.map(p => (
<h1>{p.name}</h1>
))
}
</div>
)
}
}
}
const mapStateToProps = state => ({
//products from root reducer
products: state.products,
})
export default connect(mapStateToProps, { getProducts })(Stuff);
Action
// Get all products
export const getProducts = () => dispatch => {
axios
.get('http://localhost:5000/api/products/all')
.then(res =>
dispatch({
type: GET_PRODUCTS,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_PRODUCTS,
payload: null
})
);
};
Reducer
import { GET_PRODUCTS } from '../actions/types';
const initialState = {
products: null,
loading: true
}
export default function(state= initialState, action){
switch(action.type){
case GET_PRODUCTS:
console.log('hitting GET_PRODUCTS', action.payload)
return {
...state,
products: action.payload,
loading: false
}
default:
return state
}
}
Server route
router.get('/all', (req, res) => {
Product.find()
.exec()
.then(stuff => {
res.status(200).json(stuff);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
postman result
render() {
console.log("Products from props", this.props);
const { loading, products } = this.props;
if (loading === true) {
return <div>Loading...</div>;
} else {
return (
<div>{products && products.products.map(p => <h1>{p.name}</h1>)}</div>
);
}
}
this.props.products value is { products : [..] }, so you have to access it by this.props.products.products, in cases like this it will be easier if you use destructring assignment syntax to get the respected values to avoid some confusion.
I have such action:
import { GET, POST, PUT, REMOVE } from "../../Utils/Http";
export const FETCH_ARTICLES = "FETCH_ARTICLES";
export const FETCH_ARTICLES_SUCCESS = "FETCH_ARTICLES_SUCCESS";
export const FETCH_ARTICLES_FAILURE = "FETCH_ARTICLES_FAILURE";
export const RESET_ARTICLES = "RESET_ARTICLES";
export function fetchArticles() {
const request = GET("/articles");
return {
type: FETCH_ARTICLES,
payload: request
};
}
export function fetchArticlesSuccess(articles) {
return {
type: FETCH_ARTICLES_SUCCESS,
payload: articles
};
}
export function fetchArticlesFailure(error) {
return {
type: FETCH_ARTICLES_FAILURE,
payload: error
};
}
and reducer:
import {
FETCH_ARTICLES,
FETCH_ARTICLES_SUCCESS,
FETCH_ARTICLES_FAILURE,
RESET_ARTICLES
} from "../Actions/Article";
const INITIAL_STATE = {
articlesList: {
articles: { data: [], total: 0 },
error: null,
loading: false
},
newTractor: { article: null, error: null, loading: false },
activeTractor: { article: null, error: null, loading: false },
deletedTractor: { article: null, error: null, loading: false }
};
const reducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case FETCH_ARTICLES:
return {
...state,
articleList: { articles: {}, error: null, loading: true }
};
case FETCH_ARTICLES_SUCCESS:
return {
...state,
articleList: { articles: action.payload, error: null, loading: false }
};
case FETCH_ARTICLES_FAILURE:
return {
...state,
articleList: { articles: {}, error: action.payload, loading: false }
};
case RESET_ARTICLES:
return {
...state,
articleList: { articles: {}, error: null, loading: false }
};
default:
return state;
}
};
export default reducer;
And i try it to use this way in list component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { isUndefined } from "lodash";
import {
fetchArticles,
fetchArticlesSuccess,
fetchArticlesFailure
} from "../../Store/Actions/Article";
class ArticleList extends Component {
componentDidMount() {
this.props.fetchArticles();
}
render() {
return <div className="ui segment" />;
}
}
const mapDispatchToProps = dispatch => {
return {
fetchArticles: () => {
dispatch(fetchArticles()).then(response => {
!response.error
? dispatch(fetchArticlesSuccess(response.payload.data))
: dispatch(fetchArticlesFailure(response.payload.data));
});
}
};
};
export default connect(null, mapDispatchToProps)(ArticleList);
also Http.js:
import axios from "axios";
const http = axios.create({
baseURL: process.env.BASE_API_URL
});
export const GET = (url, params) => {
return new Promise((resolve, reject) => {
http({
method: "get",
url,
params
})
.then(response => {
resolve(response);
})
.catch(err => {
console.log("GET err ", err);
reject(err);
});
});
};
...
But as result I get:
TypeError: dispatch is not a function in dispatch(fetchArticles()).then(response => {
What I do wrong?
Also how can i write this part:
fetchTractors()).then(response => {
!response.error
? dispatch(fetchTractorsSuccess(response.payload.data))
: dispatch(fetchTractorsFailure(response.payload.data));
}
in component class? is it possible? (not to move it to the mapDispatchToProps block)
i took some ideas from here: https://github.com/rajaraodv/react-redux-blog/
I can see many problems here:
const mapDispatchToProps = dispatch => {
return {
fetchArticles: () => {
dispatch(fetchArticles()).then(response => {
!response.error
? dispatch(fetchArticlesSuccess(response.payload.data))
: dispatch(fetchArticlesFailure(response.payload.data));
});
}
};
};
dispatch is a synchronous thing by default unless you have configured some middleware such as redux-thunk to handle functions. dispatch takes native object as an argument in normal scenario.
dispatch does not return a promise. So then can not be used,
connect takes first arguments as mapStateToProps and second argument as mapDispatchtoProps. There is also third argument which is not generally used. So I will not mention it for now.
4.you need to pass the actions creators through mapDispatchToProps like this:
import { bindActionCreators } from "redux"
const mapDispatchToProps = dispatch => bindActionCreators({
fetchArticles,
fetchArticlesSuccess,
fetchArticlesFailure,
}, dispatch)
The probles is here:
export default connect(mapDispatchToProps)(ArticleList);
First parameter should be mapStateToProps. But you actually can pass null:
export default connect(null, mapDispatchToProps)(ArticleList);
If someone encountered this problem while using ts + redux, the IDE prompted you that there is no then method, you can refer to this link
I am new to redux and I am having a hard time understanding how to connect the payload of my API call to my state.
Right now my action.js file looks like this:
import ApiService from '../../services/ApiService';
import { reset } from 'redux-form';
//actions
export const getStock = () => {
return {
type: 'GET_STOCK'
}
}
export const getStockPending = () => {
return {
type: 'GET_STOCK_PENDING'
}
}
export const getStockFulfilled = (stock) => {
return {
type: 'GET_STOCK_FULFILLED',
payload: stock
}
}
export const getStockRejected = () => {
return {
type: 'GET_STOCK_REJECTED'
}
}
// async function calls
export function fetchStocksWithRedux() {
const action_type = "GET_STOCK";
const stock = 'AAPL';
return (dispatch) => {
dispatch({type: `${action_type}_PENDING`});
return ApiService.get(`/search?query=${stock}`)
.then(([response, json]) =>{
if(response.status === 200){
dispatch(getStockFulfilled(json))
}
else{
dispatch(getStockRejected())
}
})
}
}
and my reducer.js file looks like this:
const initialState = {
inProgress: false,
stock: {},
stocks: ['NKE', 'AMZN', 'AAPL'],
error: {}
}
export default (state = initialState, action) => {
switch(action.type) {
case 'GET_STOCK_PENDING':
return {
...state,
inProgress: true,
error: false
}
case 'GET_STOCK_FULFILLED':
return {
...state,
stock: action.payload,
inProgress: false
}
case 'GET_STOCK_REJECTED':
return {
...state,
inProgress: false,
error: action.error
}
default:
return state;
}
}
When I go to call my method fetchStocksWithRedux in my component, the network tab in my dev tools shows a 200 status and the response I'm expecting, but the reducer dispatches the 'GET_STOCK_REJECTED' action, but the error hash is empty. What do you think is going wrong?
Here is my component, for reference:
import React, { Component } from 'react';
import { fetchStocksWithRedux } from '../../redux/modules/Stock/actions';
import { connect } from 'react-redux';
class Dashboard extends Component {
componentDidMount() {
this.props.fetchStocksWithRedux()
}
render() {
return (
<div className="uk-position-center">
</div>
)
}
}
export default connect(
state => ({
stocks: state.stocks,
stock: state.stock
})
, { fetchStocksWithRedux }
)(Dashboard);
Thanks. Any advice or guidance would be greatly appreciated!
I am still learning React-Redux. I understand how to retrieve simple JSON arrays/objects. However, I am not sure how to call a nested object. When this is bundled, I am viewing the 'after' object - t3_5t61fz - obviously because I'm calling data.after, but when I try to implement data.title nothing appears. I am trying to grab the title and am viewing this in the console:
Object
data
:
Object
data
:
Object
data
:
Object
after
:
"t3_5t61fz"
before
:
null
children
:
Array[25]
0
:
Object
data
:
Object
title
:
"How Google builds a web framework"
Reducer:
export default function reducer(state={
data: {
data: {}
},
fetching: false,
fetched: false,
error: null,
}, action) {
switch(action.type){
case "FETCH_DATA":{
return {...state, fetching:true}
}
case "FETCH_DATA_REJECTED":{
return {...state, fetching: false, error: action.payload}
}
case "FETCH_DATA_FULFILLED":{
return {...state, fetching: false, fetched: true, data: action.payload}
}
case "ADD_DATA":{
return {...state, data: [...state.data, action.payload]}
}
case "UPDATE_DATA":{
const { id, title } = action.payload
const newData = [...state.data]
const dataToUpdate = newData.findIndex(data => data.id === id)
newData[dataToUpdate] = action.payload;
return {...state, data: newData}
}
case "DELETE_DATA":{
return {...state, data: state.data.filter(data => data.id !== action.payload)}
}
}
return state
}
Action
import axios from 'axios';
export function fetchData(){
return function(dispatch){
axios.get("https://www.reddit.com/r/webdev/top/.json")
.then((response) => {
dispatch({ type: "FETCH_DATA_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_DATA_REJECTED", payload: err})
})
}
}
export function addData(id, text){
return {
type: 'ADD_DATA',
payload:{
id,
data,
},
}
}
export function updateData(id, text){
return {
type: 'UPDATE_DATA',
payload: {
id,
data,
},
}
}
export function deleteData(id){
return {
type: 'DELETE_DATA',
payload: id
}
}
Layout.js
import React from "react"
import { connect } from "react-redux"
import { fetchData } from "../actions/dataActions"
#connect((store) => {
return {
data: store.data.data
};
})
export default class Layout extends React.Component {
componentWillMount() {
this.props.dispatch(fetchData())
}
render() {
const { data } = this.props;
return <div>
<h1>{data.data.after}</h1>
</div>
}
}
I'm calling data.after, but when I try to implement data.title nothing appears
I see that data.after is an array, and you are trying to get the title of the data of the first item in that array.
You should be able to access the data in your <h1> by getting {data.data.after[0].data.title}
Im curious why the data is being so deeply nested in this fashion though.