I've used useReducer hook in react. al of my code seems right but it isn't working. Here goes my code
import React, { useEffect, useReducer, useContext } from 'react';
import reducer from '../reducers/filter_reducer';
import { useProductsContext } from './products_context';
import { LOAD_PRODUCTS } from '../actions';
const FilterContext = React.createContext();
const initialState = {
filtered_products: [],
};
const FilterProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const { listOfProducts } = useProductsContext();
useEffect(() => {
dispatch({ type: LOAD_PRODUCTS, payload: listOfProducts });
}, [listOfProducts]);
return (
<FilterContext.Provider value={{ ...state }}>
{children}
</FilterContext.Provider>
);
};
export const useFilterContext = () => {
return useContext(FilterContext);
};
export default FilterProvider;
and there goes my reducer function :
import { LOAD_PRODUCTS } from '../actions';
const filter_reducer = ({ state, action }) => {
if (action.type === LOAD_PRODUCTS) {
return { ...state, filtered_products: action.payload };
}
};
export default filter_reducer;
It seems my code is right but after running this code it shows this error:
TypeError: Cannot read property 'type' of undefined
Can someone see where I'm wrong?
Try this
const filter_reducer = ( state, action ) => {
if (action.type === LOAD_PRODUCTS) {
return { ...state, filtered_products: action.payload };
}
};
Related
I am trying to use redux keep an array of objects in the state. When I add an object to the state (variable called flash in the second snippet), It is added to the state as undefined. Can anyone tell me why?
To be clear, the intended result was to run the 'addHero()' function in the second snipped and have the state afterwards be [{name:batman}, {name:flash}].
import { configureStore, createSlice } from "#reduxjs/toolkit";
const rosterSlice = createSlice({
name: "roster",
initialState: { roster: [{ name: "batman" }] },
reducers: {
addHero(state, action) {
console.log(action.payload);
return { ...state, roster: [...state.roster, action.payload] };
},
},
});
export const actions = rosterSlice.actions;
const store = configureStore({
reducer: rosterSlice.reducer,
});
export default store;
import React, { Fragment } from "react";
import "./App.css";
import Canvas from "./components/Canvas";
import { useSelector, useDispatch } from "react-redux";
import { actions } from "./store/index";
const App = () => {
const roster = useSelector((state) => state.roster);
const dispatch = useDispatch();
const addHero = () => {
dispatch(actions.addHero());
};
const flash = {name:"flash"};
console.log(roster);
const handleClick = () => {
console.log(flash);
addHero(flash);
console.log(roster);
};
return (
<Fragment>
<h1>Roster</h1>
<h2>{roster[0].name}</h2>
<button onClick={handleClick}>Add Flash</button>
{/* <Canvas /> */}
</Fragment>
);
};
export default App;
You are not passing argument to your dispatched action
const addHero = (arg) => { // added arg
dispatch(actions.addHero(arg));
};
I've been getting an undefined in home.js when I try to console.log(products)
I tried to use postman, and it can successfully get the data from the database, also inside apicalls.js, when I try to console.log(res.data).
Am I missing something? I tried to copy in the tutorial, but it didn't work for me.
apicalls.js
import axios from 'axios'
import {
getProducstStart,
getProductsFailure,
getProductsSuccess,
} from './ProductAction'
export const getProducts = async (dispatch) => {
dispatch(getProducstStart())
try {
const res = await axios.get('/product')
dispatch(getProductsSuccess(res.data))
} catch (error) {
dispatch(getProductsFailure())
}
}
home.js
import React, { useContext, useEffect } from 'react'
import { getProducts } from '../../context/ApiCalls'
import { ProductContext } from '../../context/ProductContext'
const Home = () => {
const { products, dispatch } = useContext(ProductContext)
useEffect(() => {
getProducts(dispatch)
}, [dispatch])
console.log(products)
return (
<div className="home">
</div>
)
}
ProducetReducer.js
const ProductReducer = (state, action) => {
switch (action.type) {
case 'GET_PRODUCT_START':
return {
products: [],
isFetching: true,
error: false,
}
case 'GET_PRODUCT_SUCCESS':
return {
products: action.payload,
isFetching: false,
error: false,
}
case 'GET_PRODUCT_FAILURE':
return {
products: [],
isFetching: false,
error: true,
}
default:
return { ...state }
}
}
export default ProductReducer
ProductContext.js
import { createContext, useReducer } from 'react'
import ProductReducer from './ProductReducer'
const INITIAL_STATE = {
products: [],
isFetching: false,
error: false,
}
export const ProductContext = createContext(INITIAL_STATE)
export const ProductContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(ProductReducer, INITIAL_STATE)
return (
<ProductContext.Provider
value={{
products: state.products,
isFetching: state.isFetching,
error: state.error,
dispatch,
}}
>
{children}
</ProductContext.Provider>
)
}
index.js
import { createRoot } from 'react-dom/client'
import App from './App'
import { ProductContextProvider } from './context/ProductContext'
const container = document.getElementById('root')
const root = createRoot(container)
root.render(
<ProductContextProvider>
<App />
</ProductContextProvider>
)
I am learning react-redux, so now I am trying to create react-redux crud app, here is ny solution
Here is repo : repo demo
The button
<span className="delete_info" onClick={() => deleteComment(comment.id) }>Delete</span>
The action creator to delete element
export const removeComment = id =>{
return{
type: ActionTypes.DELETE_COMMENTS,
payload:id
}
}
// delete comments
export const deleteComment = id =>{
console.log('ids', id);
return dispatch =>{
dispatch(fetchCommentsRequest())
axios.delete(`/api/v1/todo/${id}`)
.then(response =>{
console.log('yeees mom', response.data)
dispatch(removeComment(id))
})
.catch(error =>{
const erroMsg =error.message;
console.log('eeeror', erroMsg)
dispatch(fetchCommentsFailure(erroMsg))
})
}
}
Here is my reducer
import * as ActionTypes from '../action-types'
const initialState ={
data:[],
error:'',
comments:[],
loading:false,
editing:false
}
const reducer = (state= initialState, action) => {
switch (action.type) {
case ActionTypes.FETCH_COMMENTS_REQUEST:
return{
...state,
loading: true,
}
case ActionTypes.FETCH_COMMENTS_SUCCESS:
return{
...state,
loading:false,
comments:action.payload,
error:''
}
case ActionTypes.FETCH_COMMENTS_FAILURE:
return{
...state,
loading:false,
error:action.payload
}
case ActionTypes.ADD_COMMENTS:
return{
...state,
comments:state.comments.concat(action.payload)
}
case ActionTypes.DELETE_COMMENTS:
return{
...state,
comments: state.comments.filter(comment =>comment.id !==action.payload)
}
case ActionTypes.EDIT_COMMENTS:
return{
...state,
comments: state.comments.map(comment =>comment.id === action.payload?{
...comment,
editing:!editing
}:comment)
}
default: // need this for default case
return state
}
}
export default reducer
Now when I click delete, I see on the console the ID from action creators, but the element is not removed
and no errors, what is wrong here?
There's a few knots; I made this simplified sandbox (mocking an api call) of how it should work:
https://codesandbox.io/s/wonderful-minsky-99xi4?file=/src/App.js:0-799
index
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunkMiddleware from "redux-thunk";
import rootReducer from "./rootReducer";
import App from "./App";
const store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
App
import React from "react";
import "./styles.css";
import { connect } from "react-redux";
import deleteRequest from "./deleteRequest";
const mapStateToProps = state => {
return {
comments: state.comments
};
};
const mapDispatchToProps = {
deleteRequest: deleteRequest
};
let App = ({ comments, deleteRequest }) => {
const makeDeleteRequest = id => {
deleteRequest(id);
};
return (
<div className="App">
{comments.map(comment => {
return (
<div key={comment.id}>
<p>{comment.text}</p>
<button onClick={() => makeDeleteRequest(comment.id)}>
Delete
</button>
</div>
);
})}
</div>
);
};
App = connect(
mapStateToProps,
mapDispatchToProps
)(App);
export default App;
reducer
const initialState = {
data: [],
error: "",
comments: [{ id: 1, text: "test1" }, { id: 2, text: "test2" }],
loading: false,
editing: false
};
function rootReducer(state = initialState, action) {
switch (action.type) {
case "DELETE_COMMENT":
return {
...state,
comments: state.comments.filter(comment => comment.id !== action.id)
};
default:
return state;
}
}
export default rootReducer;
async action
import deleteComment from "./deleteComment";
const mockAPI = new Promise(function(resolve, reject) {
setTimeout(() => resolve("deleted"), 2000);
});
const deleteRequest = id => {
return dispatch => {
const makeDeleteRequest = async () => {
await mockAPI;
dispatch(deleteComment(id));
};
makeDeleteRequest();
};
};
export default deleteRequest;
delete comment action
export default function deleteComment(id) {
return { type: "DELETE_COMMENT", id };
}
It looks like you're not actually dispatching the action, you're just returning an async action creator (deleteComment function). In order your code to work, you need to first add redux-thunk middleware to your redux store (so that you can use async action creators) and then, in your component, when you're calling deleteComponent, you have to wrap the call using redux dispatch.
If you're using a function component, you can add useDispatch hook and have something like:
import {useDispatch} from "react-redux";
// ...
function MyComponent() {
const dispatch = useDispatch();
// ...
return <span className="delete_info" onClick={() => dispatch(deleteComment(comment.id))}>Delete</span>
}
or you can just use the connect function to create a HOC and pass the dispatch function from the provider's context:
const ConnectedComponent = connect(undefined, dispatch => ({dispatch}))(MyComponent);
function MyComponent({dispatch}) {
return <span className="delete_info" onClick={() => dispatch(deleteComment(comment.id))}>Delete</span>;
}
I've got a React app and now I use redux. The app worked, but now I'm getting the error:
Uncaught TypeError: Cannot read property 'getState' of undefined
at new Provider (Provider.js:25)
at vf (react-dom.production.min.js:132)
at Og (react-dom.production.min.js:167)
at Tg (react-dom.production.min.js:180)
at bi (react-dom.production.min.js:232)
at ci (react-dom.production.min.js:233)
at Di (react-dom.production.min.js:249)
at Yh (react-dom.production.min.js:248)
at Xh (react-dom.production.min.js:245)
at qf (react-dom.production.min.js:243)
Can someone help here?
Code:
store.js:
import {
combineReducers,
createStore
} from 'redux';
import campaignReducer from './campaign/reducer';
const reducer = combineReducers({
campaign: campaignReducer
});
const enhancedCreateStore = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default enhancedCreateStore;
App.jsx:
import React from 'react';
import ReactDOM from 'react-dom';
import {
HashRouter as Router
} from 'react-router-dom';
import {
Provider
} from 'react-redux';
// tell webpack about all used svg images
/* eslint-disable */
import svgSprite from './js/services/helper';
/* eslint-enable */
import Shop from './js/components/shop';
import {
store
} from './js/store/store';
// render the {React.Component}
ReactDOM.render( <
Provider store = {
store
} >
<
Router >
<
Shop / >
<
/Router> < /
Provider > ,
document.querySelector('#order-form')
);
Shop.jsx (connect part):
const stateMapper = ({
isOpen,
message,
showOkBtn,
showCancelBtn
}) => ({
isOpen,
message,
showOkBtn,
showCancelBtn
});
const dispatchMapper = dispatch => ({
onSetModalOpen: options => dispatch(Action.openCampaignModal(options)),
onSetModalClosed: () => dispatch(Action.closeCampaignModal())
});
export default connect(
stateMapper,
dispatchMapper
)(Shop);
helper.js:
export const createReducer = (initialState, handlers) => (state = initialState, action) => {
// eslint-disable-next-line
if (action.type in handlers) {
return handlers[action.type](state, action);
}
return state;
};
export const multiUse = (reducer, name = '') => (state = null, action) => {
if (action.name !== name) return state;
return reducer(state, action);
};
action.js:
export const OPEN_CAMPAIGN_MODAL = 'OPEN_CAMPAIGN_MODAL';
export const CLOSE_CAMPAIGN_MODAL = 'CLOSE_CAMPAIGN_MODAL';
export const SET_CAMPAIGN = 'SET_CAMPAIGN';
export const openCampaignModal = ({
message,
showOkBtn,
showCancelBtn
}) => ({
type: OPEN_CAMPAIGN_MODAL,
modal: {
isOpen: true,
message,
showOkBtn,
showCancelBtn
}
});
export const closeCampaignModal = () => ({
type: CLOSE_CAMPAIGN_MODAL,
modal: {
isOpen: false
}
});
export const setCampaign = name => ({
type: SET_CAMPAIGN,
selected: name
});
reducer.js:
import {
createReducer
} from '../helper';
import * as Action from './actions';
export default createReducer({
modal: {
isOpen: false,
message: null,
showOkBtn: true,
showCancelBtn: false
},
selected: ''
}, {
[Action.OPEN_CAMPAIGN_MODAL]: (state, {
isOpen,
message,
showOkBtn,
showCancelBtn
}) =>
Object.assign({}, state, {
modal: {
isOpen,
message,
showOkBtn,
showCancelBtn
}
}),
[Action.CLOSE_CAMPAIGN_MODAL]: (state, {
isOpen
}) =>
Object.assign({}, state, {
modal: {
isOpen
}
}),
[Action.SET_CAMPAIGN]: (state, action) =>
Object.assign({}, state, {
selected: action.selected
})
});
What is the problem here? I did a lot of debugging and the redux-dev-tools in chrome also seems to show an initialized redux state (although I cannot see any state).
You are export default in store.js, but then you use a named import:
import {
store
} from './js/store/store';
Change to this:
import store from './js/store/store';
I'm using React-Laravel for my project.
The problem is when I tried to use redux-thunk for the asynchronous dispatch function.
My dispatch function won't get executed.
Please do help me figure out this problem.
I have already tried to use promise or redux-devtools-extension library
https://codeburst.io/reactjs-app-with-laravel-restful-api-endpoint-part-2-aef12fe6db02
app.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import Layout from './jsx/Layout/Layout';
import marketplaceReducer from './store/reducers/marketplace';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const appReducer = combineReducers({
marketplace: marketplaceReducer
});
const rootReducer = (state, action) => {
return appReducer(state, action);
}
const store = createStore(rootReducer, composeEnhancers(
applyMiddleware(logger, thunk)
));
const render = (
<Provider store={store}>
<BrowserRouter>
<Layout />
</BrowserRouter>
</Provider>
);
ReactDOM.render(render, document.getElementById('root'));
marketplace.js (action)
import * as actionTypes from './actionTypes';
import axios from '../../axios';
export const loadMarketplace = () => {
console.log("Load Marketplace");
return {
type: actionTypes.LOAD_MARKETPLACE
};
}
export const successMarketplace = (data) => {
console.log("Success Marketplace");
return {
type: actionTypes.SUCCESS_MARKETPLACE,
data: data
}
}
export const failedMarketplace = () => {
console.log("Failed Marketplace");
return {
type: actionTypes.FAILED_MARKETPLACE
}
}
export const showMarketplace = () => {
console.log("Show Marketplace Action")
return dispatch => {
//This is the problem
//Inside this function, I can't see any console.log, even loadMarketplace() didn't get called.
console.log("Show Marketplace in dispatch");
dispatch(loadMarketplace());
axios.get('/marketplaces')
.then(response => {
dispatch(successMarketplace(response));
})
.catch(error => {
dispatch(failedMarketplace());
});
};
}
marketplace.js (reducer)
import * as actionTypes from '../actions/actionTypes';
const initial_state = {
data: [],
loading: false
}
const loadMarketplace = (state, action) => {
console.log("Load Marketplace Reducer")
return {
...state,
loading: true
};
}
const successMarketplace = (state, action) => {
console.log("Success Marketplace Reducer", action.data)
return {
...state,
loading: false,
data: action.data
};
}
const failedMarketplace = (state, action) => {
return {
...state,
loading: false
};
}
const reducer = (state = initial_state, action) => {
//This is called when the first init, never got it through showMarketplace() function.
console.log("Marketplace Reducer", action);
switch (action.type) {
case actionTypes.LOAD_MARKETPLACE: return loadMarketplace(state, action);
case actionTypes.SUCCESS_MARKETPLACE: return successMarketplace(state, action);
case actionTypes.FAILED_MARKETPLACE: return failedMarketplace(state, action);
default: return state;
}
}
export default reducer;
Marketplace.js (jsx view)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../store/actions';
class Marketplace extends Component {
componentDidMount() {
console.log('[ComponentDidMount] Marketplace')
this.props.showMarketplace();
}
render() {
return (
<React.Fragment>
Marketplace
</React.Fragment>
);
}
}
const mapDispatchToProps = dispatch => {
return {
showMarketplace: () => dispatch(actions.showMarketplace)
};
}
export default connect(null, mapDispatchToProps)(Marketplace);
This is the result of my console.log (when loading the first time for Marketplace.js)
Please do help, I've been struggling for 2 hours or more, only because of this problem. (This is my first time using React-Laravel).
Thank you.
I already found the problem. It is not redux-thunk problem.
It is actually a normal Redux problem we found anywhere.
Marketplace.js (jsx view)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../store/actions';
class Marketplace extends Component {
componentDidMount() {
console.log('[ComponentDidMount] Marketplace')
this.props.showMarketplace();
}
render() {
return (
<React.Fragment>
Marketplace
</React.Fragment>
);
}
}
const mapDispatchToProps = dispatch => {
return {
showMarketplace: () => dispatch(actions.showMarketplace) //THIS IS THE PROBLEM, IT IS NOT EXECUTING PROPERLY. THIS ONE SHOULD BE
showMarketplace: () => dispatch(actions.showMarketplace()) //SHOULD BE LIKE THIS.
};
}
export default connect(null, mapDispatchToProps)(Marketplace);
Edited: I think it is something about thunk is not added right to redux.
First of all try to add only thunk.
const store = createStore(rootReducer, composeEnhancers(
applyMiddleware(thunk)
));
If it works, maybe try to change the order of them.