I have this reducer
import { FETCH_WEATHER } from "../actions/index";
export default function(state = [], action) {
switch (action.type) {
case FETCH_WEATHER:
console.log(state)
return [action.payload.data,...state];
default:
return state
}
}
but whenever I console.log the state the result is always undefined
and here is my action
import axios from 'axios';
const API_KEY = '...';
const ROOT_URL = `...`
export const FETCH_WEATHER = 'FETCH_WEATHER';
export function fetchWeather(city){
const url = `${ROOT_URL}&q=${city},us`;
const request = axios.get(url)
return {
type : FETCH_WEATHER,
payload: request,
}
}
I'm thinking to create a constant to hold the data overall and just pass it in reducer but I don't think it's the right approach.
Question:
How can I access the previous state in the reducer so that whenever I console.log there is a value.
Here is the code codesandbox
Previous state is stored in your state variable, as defined in your reducer. Previous state can therefore be accessed in your reducer via the state variable.
It looks like you have a block syntax error that may be causing you problems (see correction below).
Also, if you want to log previous state to the console when ever your reducer is run, consider placing the call to console.log at the beginning of your reducer:
import { FETCH_WEATHER } from "../actions/index";
export default function(state = [], action) {
// console.log(state) // Add this to log previous state for every call to reducer
switch (action.type) {
case FETCH_WEATHER: { // Added { here
console.log(state)
return [action.payload.data,...state];
} // Added } here
default:
return state
}
}
Related
I am new to react and redux ... so please forgive me for noobie mistakes. I read several documentations of redux and came to conclusion that this is how i should store state of react component. I require redux because there are many more nested components which need quick access of data. however... when I try to export store ... I can't find how to do so.
My App.js
export default class App extends Component {
state = {
xx: null,
yy: null
}
componentDidMount(){
//some logic
// State gets data from api
this.setState({
xx: someval,
yy: someval2
});
}
render() {
const obj = {
xx: this.state.xx,
yy: this.state.yy
};
userReducer(obj,updateUserDetails());
const store = createStore(userReducer);
return (
<Provider store={store} >
<UserDetails props ={this.state} />
</Provider>
);
}
}
// Reducer function
export const userReducer = (state, action) => {
console.log("in reducer " + JSON.stringify(state));
switch(action.type) {
case 'UPDATE_USER_INFO':
state = {
...state,
xx: action.payload.xx,
yy: action.payload.yy
}
break;
}
return state;
}
// Doesn't work
export const store = createStore(userReducer)
// Action
export const updateUserDetails = () => {
return {
type: 'UPDATE_USER_INFO'
}
}
I can't figure out way to export store so that it is accessible to nested components. Kindly help
Thanks in advance!
From looking on your code, I can see a few issues that each one can be your problem.
while reducer first loads, it has to hold initial value for the state to begin with, not related to the one you want to be set at component mount
// assiging empty obj as initial value
export const userReducer = (state = {}, action)
Actions of redux are higher order functions, returning object to be dispatch
export const updateUserDetails = () => (dispatch, getState) => {
return dispatch ({
type: 'UPDATE_USER_INFO'
})
}
About your createStore, declare here as well initial value
// assign empty obj as initial value
export const store = createStore(userReducer, {})
hope it is helpful, anyhow I recommended on looking through the docs again
We have set up a project with redux. In this project, we get an info objecat from an api and insert it into the store. Now we noticed that the function components re-render even if the api return the same state as in the previous request.
We think it's because we are overwriting the store but we are not sure.
ChatContainer.js
const mapStateToProps = function (state) {
return {
content: state.info.content,
loading: state.info.loading,
}
}
const ChatContainer = connect(
mapStateToProps,
)(Chat)
export default ChatContainer
Chat.js
function Chat(props) {
const { content, loading } = props;
return (
<Info content={content} loading={loading} />
)
}
action.js
export function setInfo(info) {
return {
type: SET_INFO, info: {
content: info,
loading: false
}
}
}
reducer.js
function setInfo(state = { content: [], loading: true }, action) {
switch (action.type) {
case SET_INFO:
return action.info
default:
return state
}
}
const appReducer = combineReducers({
...
info: setInfo,
...
})
export default appReducer
If state.info.content is an object, every time you change it with setInfo it will have a new reference. React-redux does a shallow compare on the result of mapStateToProps, so if your content is a different reference every time your component will re-render. connect HOC has an options parameter that you can use to implement a custom compare.
My advice would be to add a check to your setInfo or to the code calling setInfo and not calling your API if data is already loaded/didn't change(don't know your business logic).
I am getting an error while using combineReducer method in redux (redux#3.7.2). Same code will work when I am using only one reducer.
Running code here
Code
const { createStore, combineReducers, applyMiddleware } = require ('redux')
const aReducer = (state, action) => {
switch (action.type) {
case 'A':
{
return { ...state };
}
default: return state;
}
return state;
}
const bReducer = (state, action) => {
switch (action.type) {
case 'B':
{
return { ...state };
}
default: return state;
}
return state;
}
const configureStore = (initialState) => {
let rootReducer = combineReducers({ a:aReducer, b:bReducer });
console.log('configureStore', initialState);
const str = createStore(rootReducer, initialState);
return str;
};
const store = configureStore({});
console.log('store',store);
store.subscribe(() => {
console.log(store.getState());
});
In the create store line, if i am replacing the rootReducer to aReducer, this code wont have any problem. I did not understand why the reducers returning undefined state, i am passing initial state as a plane object.
There are two things going on here. Firstly, combineReducers also combines the states of each reducer in an object with the same keys as the argument reducers object, so to initialize each state correctly you'll need:
const store = configureStore({a: {}, b: {}});
This is not enough to fix the problem though, as combineReducers also requires that each reducer can handle state undefined and never returns undefined
(see the docs). If it can't you get this error:
Error: Reducer "..." returned undefined during initialization. If the state passed to the
reducer is undefined, you must explicitly return the initial state. The initial state may
not be undefined. If you don't want to set a value for this reducer, you can use null
instead of undefined.
The confusing thing about this is that the check is done when combineReducers is called (ie. before state initialization,) but the error isn't shown until the reducer is used. This means that even if you initialize the state correctly, and your reducers never receive state undefined, you'll still get the error if the reducers can't handle it.
To fix it, replace (state, action) => { in each reducer by (state = {}, action) => {, or explicitly return null if state is undefined. Note that these initial states are only used if you don't pass an initial state to createStore. To prevent confusion, I usually do all initialization in the reducers and not at createStore.
I've tried all of the related questions here in Stack Overflow and still didn't find a solution to this problem.
I have a reducer called me and I'm trying to update an array of objects in it called folders, whenever I update the me reducer the component doesn't update.
Here's how I'm updating the reducer in my component:
class ComponentA extends Component {
...
updateUploadedFiles(file) {
console.log(this.props.store);
const newFolders = this.props.me.folders.map(
folder =>
folder._id === file.parent._id
? {
...folder,
files: [...folder.files, file.file]
}
: folder
);
this.props.updateMe({
...this.props.me,
folders: newFolders
});
}
...
}
function mapStateToProps(state) {
return {
me: state.me,
path: state.path,
filesToUpload: state.uploads
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
updatePath,
updateMe,
updateUploads
},
dispatch
);
}
export default connect(mapStateToProps, mapDispatchToProps, null, {
pure: false
})(Upload);
this is my updateMe action's code:
export const updateMe = state => ({
type: "UPDATED_ME",
payload: state
});
And this is the me reducer's code:
export default function(state = "NOT_AUTHENTICATED", action) {
switch (action.type) {
case "UPDATED_ME":
return action.payload;
default:
return state;
}
}
Also here's how I'm combining the reducers:
import me from "./me";
...
import { combineReducers } from "redux";
const reducers = combineReducers({
me,
...
});
export default reducers;
This is not how redux works.
In order to update any part of your Redux store you must dispatch an action in order to let Redux "know" that the store changed and update any dependent component.
You state object must be immutable.
Redux has proven a little tricky for me to wrap my head around, and I was wondering if someone could help point me in the right direction of what piece I am not grasping to get my desired results. Just a forewarning: I am using ES6 syntax.
Okay, so I have setup somewhat of a sandbox to test out how redux works, and this is the current file setup I am working with.
-actions
--index.js
-reducers
--index.js
--reducer_user.js
-containers
--ReduxTest.js
In my container, ReduxTest.js, I have the following code.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUser } from '../actions/index';
class ReduxTest extends Component {
render() {
return (
<div>
{console.log(this.props.fetchUser())}
{console.log(this.props.user)}
</div>
)
}
}
export default connect( null, { fetchUser } ) (ReduxTest);
When I render ReduxTest.js to the screen, the first console.log statement shows up as,
Object { type: "FETCH_USER", payload: "This is just a test."}
The second one however, shows up as "undefined".
Here is what my actions index.js looks like,
export const FETCH_USER = 'FETCH_USER';
export function fetchUser() {
const testing = "This is just a test.";
return {
type: FETCH_USER,
payload: testing
}
}
Here is my reducer_user.js file
import { FETCH_USER } from '../actions/index';
export default function(state = null, action) {
switch(action.type) {
case FETCH_USER:
return action.payload;
}
return state;
}
and finally, here is my index.js in the reducer folder
import { combineReducers } from 'redux';
import UserReducer from './reducer_user';
const rootReducer = combineReducers({
user: UserReducer
});
export default rootReducer;
I am using a video tutorial from Udemy, so that is where I am getting some of my syntax and what not. I was under the impression that I would be able to access "this.props.user" from the index.js reducer, but I am doing something wrong, or missing a step. Any help would be appreciated.
Just so I am clear, all my intention is, is to successfully have the ReduxTest container console log JUST the string that is in the payload. if you can help with that, I think I can carry it on from there. Thanks =)
You're only passing the action creator to your component. If you want to access your props.user than you have to provide it. You can achieve this by the first argument of the connect function.
const mapStateToProps = state => ({
user: state.user,
});
export default connect(mapStateToProps, { fetchUser })(ReduxTest);
The first argument of connect must be a callable function. The only argument of this function is the current state. The function must return an object, containing all properties you want to access inside your component.
Please notice that the state of your user reducer is set to null initially. Redux fires multiple, internal actions. If you log your current state in your render method, it can happen, that your state gets logged before you are calling your own actions. This can be confusing.
You can change the initial state of your reducer this way:
import { FETCH_USER } from '../actions/index';
export default function(state = 'User not fetched yet', action) {
switch(action.type) {
case FETCH_USER:
return action.payload;
}
return state;
}