store imageI am going to store the data into the react-redux-store but it is not getting stored. I don't understand what I am missing...I have given my code below.
i am trying to store the data from the api but it is not working...
INDEX.JS
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Provider } from "react-redux";
import { store } from "./features/store";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
STORE.JS
import { configureStore } from "#reduxjs/toolkit";
import moviesReducer from "./movies/movieSlice";
export const store = configureStore({
reducer: moviesReducer,
});
MOVIE SLICE.JS
import { createSlice } from "#reduxjs/toolkit";
const initialstate = {
movies: [],
};
const movieSlice = createSlice({
name: "movies",
initialstate,
reducers: {
addMovies: (state, { payload }) => {
state.movies = payload;
},
},
});
export const { addMovies } = movieSlice.actions;
// export const getAllMovies = (state) => state.movies.movies;
export default movieSlice.reducer;
COMPONENT
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import MovieAPI from "../config/MovieAPI";
import { addMovies } from "../features/movies/movieSlice";
const Home = () => {
const dispatch = useDispatch();
const fetchMovies = async () => {
const response = await MovieAPI.get(`?apiKey=1234&s=harry&type=movie`);
console.log(response.data);
dispatch(addMovies(response.data));
};
useEffect(() => {
fetchMovies();
}, []);
For the very first: createSlice expecting to recieve object with property named initialState instead initialstate, notice camelCase naming.
The next one: acording to location and slice name "movies" I may suspect you should define it as: const initialState = [];, due to it is "movies slice" initial state definition itself, otherwise you will have state with something like
state = {movies: {movies: []}}.
Also, you may wish to rewrite addMovies reducer in something like:
addMovies: (moview_slice_state, { payload }) => {
console.log("add movies", payload);
moview_slice_state.push(...payload);
}
where moview_slice_state - state of movies slice of whole state, e.g. state.movies.
By the way, due to #reduxjs/toolkit use immer under the hood you may "modify" state OR return new state, as Andrej KirejeÅ propose. But NOT the both of them.
P.S. For the future, feel free to create minimal demo for your question or answer, some thing like live demo based on your code
return new state:
addMovies: (state, { payload }) => ({
...state,
movies: payload
}),
by the way, how do you know it is not stored. Please, show the code where you use state data to render some component.
Related
I am making api call in my redux action creator storing those data into my redux state, but when trying to access that using useSelector getting undefined.
Not able to understand where i am doing wrong. Any help here would be great. I am using redux toolkit.
Attaching snipped for reducers, action creator, component, redux state snapshot from redux dev tools and debugging logs
Note: appID will come from path params but for now i am hardcoding it
In my SuggestedAudience snipped if i comment out {app.name} which is causing the problem then my redux states are loaded correctly, snapshot attached
Observation: If i am making any changes to Suggested audience and saving then i am getting value but when reloading it 2-3 times again the error comes.
//apps slice to store apps
import { createSlice } from "#reduxjs/toolkit";
import _ from "lodash";
const appsSlice = createSlice({
name: "apps",
initialState: {},
reducers: {
appSuccess(state, action) {
const apps = _.keyBy(action.payload.data, "uuid");
return apps;
},
},
});
export const appsAction = appsSlice.actions;
export default appsSlice.reducer;
//ui-slice to store app data
import { createSlice } from "#reduxjs/toolkit";
const uiSlice = createSlice({
name: "ui",
initialState: { appId: null, orgId: null },
reducers: {
setAppId(state, action) {
state.appId = action.payload;
},
},
});
export const uiAction = uiSlice.actions;
export default uiSlice.reducer;
//App.js
import logo from "./logo.svg";
import "./App.css";
import PageHeader from "./components/PageHeader";
import SuggestedAudiences from "./components/SuggestedAudiences";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { uiAction } from "./components/store/ui-slice";
import { appsAction } from "./components/store/apps-slice";
import { fetchApps } from "./components/store/apps-action";
function App() {
const dispatch = useDispatch();
const appID = "52f14657d6c765401a2d7d4-41dfb38a-4262-11e3-9166-005cf8cbabd8";
useEffect(() => {
dispatch(uiAction.setAppId(appID));
dispatch(fetchApps());
}, []);
return (
<>
<PageHeader />
<SuggestedAudiences />
</>
);
}
export default App;
//Suggested Audiences
import React, { useEffect } from "react";
import { fetchApps } from "./store/apps-action";
import { useDispatch, useSelector } from "react-redux";
import { uiAction } from "./store/ui-slice";
const SuggestedAudiences = (props) => {
const dispatch = useDispatch();
const appId = useSelector((state) => state.ui.appId);
const app = useSelector((state) => state.apps[appId]);
return (
<>
<div>Suggested Audiences 🚀</div>
{app.name}
</>
);
};
export default SuggestedAudiences;
Fixed it by doing {app && app.name} or {app?.name} would also work.
Keeping it open if someone can help me understand it why it is happening and is there any other way to fix this so that undefined doesn't come in first place itself.
Try to change this line
const app = useSelector((state) => state.apps[appId]);
into this
const app = useSelector((state) => state?.apps[appId]);
I cant for the life of my get one of my actions to call the reducer.
I've written multiple other actions and reducers in this app, which works perfectly.
It is the beginning of a filter function. I have a text input field, where I constantly keep track of the input field state, and dispatch the field value to the redux action. I have a console.log inside the action, which logs every keypress properly.
What I cant seem to understand, is why the reducer isn't called at each keypress. I've tried multiple console.log's inside the reducer, however none of them gets logged with keypresses.
The first console.log inside the reducer is called when I refresh the page.
if I try to log action.type instead, I get:
##redux/PROBE_UNKNOWN_ACTION1.0.i.0.0.9
If I try the same in any of the other reducers I've written in the same app, I get the appropriate type logged out.
Some code:
Filter Component:
import React, { useState } from 'react'
import { useDispatch } from 'react-redux';
import { filterAnecdotes } from '../reducers/filterReducer';
const Filter = () => {
const [value, setValue] = useState("");
const handleChange = (e) => {
setValue(e.target.value)
}
useDispatch(filterAnecdotes(value));
const style = {
marginBottom: 10
}
return (
<div style={style}>
filter <input onChange={handleChange} />
</div>
)
}
export default Filter
Reducer and action:
Here, I haven't figured out how to get the state of all anecdotes, and what to actually return. For now, I'm just trying to have it get called properly.
const filterReducer = (state = null, action) => {
// These logs only get logged on refresh.
// The action.type should be 'SEARCH', but is not.
console.log("From filterReducer")
console.log(action.type)
switch(action.type) {
case 'SEARCH':
// This is not called at all.
console.log(action.type, action.data)
return action.data;
default:
return state
}
}
export const filterAnecdotes = (filter) => {
console.log(filter);
return {
type: 'SEARCH',
data: filter
}
}
export default filterReducer;
Example of a redux file that actually works:
const reducer = (state = [], action) => {
console.log(state, action)
switch(action.type){
case 'NEW_ENTRY_NOTIFICATION':
console.log(action.type)
return [...state, action.data]
case 'NEW_VOTE_NOTIFICATION':
return [...state, action.data]
case 'HIDE_NOTIFICATION':
return []
default:
return state
}
}
export const createNewEntryNotification = (notification) => {
return {
type: 'NEW_ENTRY_NOTIFICATION',
data: notification
}
}
export const createNewVoteNotification = (notification) => {
return {
type: 'NEW_VOTE_NOTIFICATION',
data: notification
}
}
export const hideNotification = () => {
return {
type: 'HIDE_NOTIFICATION'
}
}
export default reducer
App component (should be irrelevant)
import React from 'react';
import NewEntry from './components/AnecdoteForm'
import AnecdoteList from './components/AnecdoteList'
import Notification from './components/Notification'
import Filter from './components/Filter';
const App = () => {
return (
<div>
<h2>Anecdotes</h2>
<Filter />
<Notification />
<AnecdoteList />
<NewEntry />
</div>
)
}
store (should be irrelevant)
import anecdoteReducer from './anecdoteReducer';
import notificationReducer from './notificationReducer';
import filterReducer from './filterReducer';
import { combineReducers } from 'redux'
const reducer = combineReducers({
anecdotes: anecdoteReducer,
notifications: notificationReducer,
filters: filterReducer,
});
export default reducer
index.js (should also be irrelevant)
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './App'
import reducer from './reducers/store'
const store = createStore(reducer)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
export default App
I'll be happy to provide more information if needed.
This is for a project from fullstackopen.
Try to instantiate useDispatch like this after const [value, setValue] = useState("");
const dispatch = useDispatch();
And then use the intance to dispatch the action
dispatch(filterAnecdotes(value));
the use of useDispatch is misunderstood.
Link for reference: https://react-redux.js.org/api/hooks#usedispatch
You should create a dispatch from useDispatch:
const dispatch = useDispatch();
dispatch(filterAnecdotes(value));
I have been reading several documents and watching videos regarding React Redux, but since all of them are different I wasn't able to apply that knowledge to some real project.
I will try to enumarate the process in order to use React Redux together.
Directory Structuring
project
src
components
User
index.js (Container component)
page.js (Presentational component)
actions
users.js
index.js (exports actionCreators combination)
reducers
users.js
index.js (exports reducer combination with combineReducers
constants
actionTypes.js
services
users.js
index.js
store.js
public
index.html
Redux Setup
We create constants in project/src/constants/actionTypes.js:
export const CREATE_USER = 'CREATE_USER';
export const DELETE_USER = 'DELETE_USER';
export const UPDATE_USER = 'UPDATE_USER';
We create actionCreators en project/src/actions/users.js y luego se combinan en project/src/actions/index.js:
users.js
import { CREATE_USER } from '../constants/actionTypes';
export default function createUser(user) {
type: CREATE_USER,
user
}
index.js
import { createUser } from './users';
export default {
createUser
}
We create reducers in project/src/reducers/users.js and they are combined in project/src/reducers/index.js using combineReducers():
users.js
import { CREATE_USER, UPDATE_USER, DELETE_USER } from '../constants/actionTypes';
import { createUser } from '../services/users';
const initialState = {
name: '',
password: '',
email: ''
}
export default function users(state = initialState, action) {
switch (action.type) {
case CREATE_USER:
state = createUser(action.user);
return state;
}
}
index.js
import users from './users';
export default combineReducers({
users
})
We create store in project/src/store.js:
import { createStore } from 'redux';
import reducers from './reducers';
export const store = createStore(reducers);
React Redux Setup
We wrap component application <Provider> in project/src/index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './store';
const Root = () => (
`
<Provider store={store}>
<App />
</Provider>
`
)
ReactDOM.render(Root, document.getElementById('root');
We transform component state to properties with mapStateToProps in project/src/components/User/index.js:
import React, { Component } from 'react';
import { createUser } from '../../actions/users';
import Page from './page';
class User extends Component {
render() {
return <Page users={this.props.users} />
}
}
const mapStateToProps = state => ({
users: this.props.users
// what is mapped here?
});
const mapDispatchToProops = dispatch => ({
// what about here?
});
export default connect(mapStateToProps, mapDispatchToProps)(User);
So, the question would be, is this React-Redux cycle well formed? What is missing or wrong?
Yes, the folder structure works well. As for the "fetch" or "service" functionality you're talking about, I'll give you an example of what actions and reducers both should, in a basic example, do.
So if you're working with a backend which you're "fetching" anything from, I'd recommend adding that functionality in the action, not the reducer:
import { USERS_FETCHED } from '../constants/actionTypes';
import { baseUrl } from "../constants/baseUrl";
const usersFetched = users => ( { // action to dispatch
type: USERS_FETCHED,
users,
} );
export const fetchUsers = () => ( dispatch ) => { // export for mapDispatchToProps
request( `${ baseUrl }/users` )
.then( response => {
dispatch( usersFetched( response.body ) ); // dispatch the action to reducer
} )
.catch( console.error );
}; // in your case you import createUser(), but it works either way
Now the action is concerned with functionality, in contrast the reducer is only concerned with managing the Redux state:
import { USERS_FETCHED } from "../constants/actionTypes";
export default ( state = null, action = {} ) => {
switch ( action.type ) {
case USERS_FETCHED:
return action.users;
default:
return state;
}
};
Functionality in the reducer is fine, but it should only be concerned with managing state. You can imagine how cluttered the code could get if you start fetching any data here, not to mention problems with asynchronicity. Of course, this is just one way to do it, but it works solidly. Hope this helps you in some way.
in my react App i'm using redux with redux-thunk.right now i'm getting props in my component but i'm unable to access latest props in my component methodsso i used componentWillReceiveProps to get latest props using nextprops then i'm saving nextprops into my states but the problem here is setState is asynchronous so when i'm fetching particular state in class methods,getting prev state value instead of nextprops value which is saved in state. but when i'm console those state in class methods using setInterval getting latest state value because setState value now saved.below is my code
Action creator
export function pickup(latlng) {
return function(dispatch) {
dispatch({ type: PICKUP_STATE,payload:latlng });
};
}
Reducer
import {
PICKUP_STATE,
PICKUP_ADD,
DROPOFF_STATE
} from '../actions/types';
export default (state={},action) => {
const INITIAL_STATE = {
pickup: '',
pickupAdd:''
};
switch(action.type) {
case PICKUP_STATE:
console.log(action.payload)
return {...state,pickup:action.payload};
case PICKUP_ADD:
return{...state,pickupAdd:action.payload};
case DROPOFF_STATE:
return {...state,dropoff:action.payload}
default:
return state;
}
//return state;
}
component
import {
connect
} from "react-redux";
import * as actions from "../actions"
class Map extends React.Component {
componentWillReceiveProps(nextprops) {
if (nextprops.pickupProps !== undefined) {
this.setState({
pick: nextprops.pickupProps
}, () => {
console.log(this.state.pick);
});
}
}
isPickEmpty(emptyPickState) {
this.props.pickup(emptyPickState);
// setTimeout(() =>{ console.log('sdkjlfjlksd',this.state.pick)
},3000);
console.log(this.state.pick);
}
}
const mapStateToProps = (state) => {
// console.log(state.BookingData.pickup);
return {
pickupProps:state.BookingData.pickup,
pickupAddProps: state.BookingData.pickupAdd
}
}
export default connect(mapStateToProps,actions)(Map);
App Root file
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import "normalize.css/normalize.css"
import "./styles/styles.scss";
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import reduxThunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import AppRouter from './routers/AppRouter';
import reducers from './reducers';
import {AUTH_USER} from "./actions/types";
const middleware = [
reduxThunk,
];
const store = createStore(reducers, composeWithDevTools(
applyMiddleware(...middleware),
// other store enhancers if any
));
const token = localStorage.getItem('token');
if(token){
store.dispatch({type:AUTH_USER});
}
ReactDOM.render(
<Provider store={store}>
<AppRouter />
</Provider>
, document.getElementById('app'));
1- how can i access latest props in my class methods
OR
2- how can i access nextprops setState value in my class methods
OR
3- any best way to solve this situation
please any one help me out from this situation, i'm stuck in from 3 days
If I understand it correctly, you still need to add a maps to dispatch to get the updated states from the store. The action creator still needs to be called and then mount it to your class method using componenetsDidMount
componentDidMount() {
this.props.fetchPickUp();
}
const mapDispatch = dispatch => {
return {
fetchPickUp: () => dispatch(pickUp()),
};
I have a react component that makes an AJAX call in componentWillMount and backs the data received in response to a redux store. Here is code
componentWillMount() {
var self = this;
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
console.log(this.responseText);
var json = JSON.parse(this.responseText);
var data = {
json
};
self.props.dispatch({
type: "ADD_Exams",
data
});
}
};
xmlhttp.open("GET", "http://127.0.0.1:8000/getExams/", true);
xmlhttp.send();
}
In the reducer, I am assigning the data received in action to an array defined in the reducer state.
const initialState = {
exams:[]
}
const examreducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_Exams":
return {
...state,
exams: [...state.exams, action.data.json]
};
default:
return state;
}
};
But when I use mapStateToProps to read exams variable I get undefined.
const mapStateToProps = (state) => {
return {
exams: state.exams
}
}
export default connect(mapStateToProps)(Exam);
I am creating store like this
import { Provider } from "react-redux";
const store = createStore(loginReducer, examReducer);
ReactDOM.render(
<Provider store={store}>
<Exam />
</Provider>,
document.getElementById("root")
);
registerServiceWorker();
console.log(this.props.exams) prints undefined. What is the problem here?
I ran into the same problem. My mistake was because I was doing wrong export/import of the component.
export:
export class MyComponent extends React.Component
...
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
import:
import { MyComponent } from './MyComponent';
to solve I removed the export on the class and used the default:
import MyComponent from './MyComponent';
I spent countless hours finding what was wrong, why my props werent getting any information.
I followed #Romain Pellerin and was able to discovered something
I originally had my mapStateToProps like this:
const mapStateToProps = (state) => {
return {
userInfo: state.userInfo,
loading: state.loading,
error: state.error
};
}
Following Romain's answer, i console logged and watched the output of my state object, and found that i had to specify the specific reducer that had the state that i needed to access! Because i had used combineReducers, maybe you have to specify which reducer you want to access in mapStateToProps. Soo i had to change my code to this:
const mapStateToProps = (state) => {
console.log(state);
return {
userInfo: state.THE_SPECIFIC_REDUCER.userInfo,
loading: state.THE_SPECIFIC_REDUCER.loading,
error: state.THE_SPECIFIC_REDUCER.error
};
}
Heres my code where i combine the reducers:
const rootReducer = combineReducers({
loginReducer,
THE_SPECIFIC_REDUCER
});
Soo, given that i was trying to get "THE_SPECIFIC_REDUCER" state, i had to specify it in the mapStateToProps like this:
myProp: state.THE_SPECIFIC_REDUCER.theThingIWantToAccess
I hope this works for someone!
Can you edit your mapStateToProps like this to see the actual content of your state?
const mapStateToProps = (state) => {
console.log(state);
return {
exams: state.exams
}
}
I suspect your reducer is not at the root of your reducers. If so, you might need to do state.examreducer.exams.
Also, is your component wrapped a in Provider? You need it to access the context (through which your state is accessible).
UPDATE
Make sure to initialize your store before rendering any React component.
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
const store = createStore(combineReducers({loginReducer, examReducer}), {loginReducer:{}, examReducer:{}});
ReactDOM.render(
<Provider store={store}>
<Exam />
</Provider>,
document.getElementById('root')
)
Then update your mapStateToProps:
const mapStateToProps = (state) => {
return {
exams: state.examReducer.exams || []
}
}
I think there's an issue in how you retrieve data or initialize store. Try this:
import { createStore, combineReducers } from 'redux'
import loginReducer from '../path';
import examReducer from '../path';
const rootStore = combineReducers({
loginReducer,
examReducer
})
const store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<Exam />
</Provider>,
document.getElementById("root")
);
and then:
const mapStateToProps = (state) => {
return {
exams: state.examReducer.exams
}
}