I have made component Notification and I want its action and reducer to be available to all the app.
But I am not getting why my action is saying not defined.
Here is the code
import { SHOW_NOTIFICATION, SHOW_NOTIFICATION_FULFILLED } from 'constants/actionTypes'
// notification
const initialState = {
notification: {}
}
export function fetchNotification (data) {
return {
type: 'SHOW_NOTIFICATION',
data: data
}
}
export default function notificationReducer (state = initialState, action) {
switch (action.type) {
case SHOW_NOTIFICATION_FULFILLED: {
return {
...state,
notification: action.data
}
}
}
return state
}
SHOW_FULFILLED is showing defined but never used so whats the mistake
You call action with SHOW_NOTIFICATION but reducer handle only SHOW_NOTIFICATION_FULFILLED. Use same action type in reducer as in action.
// notification
const initialState = {
notification: {}
}
export function fetchNotification (data) {
return {
type: 'SHOW_NOTIFICATION', // <--
data: data
}
}
export default function notificationReducer (state = initialState, action) {
switch (action.type) {
case SHOW_NOTIFICATION: { // <--
return {
...state,
notification: action.data
}
}
}
return state
}
Related
How do I get the state of this? I only need to put it as false and true the time I want at my components, but i`m doing something wrong, i know how do it when calling an API, but not like this.
I have this actions:
import { HIDE_MENU, ESTADO_MENU } from "./types";
export const hideMenu = dispatch => {
return dispatch({
type: HIDE_MENU
});
};
export const estadoDoMenu = open => dispatch => {
dispatch({
type: ESTADO_MENU
});
};
and this reducer:
import { HIDE_MENU, ESTADO_MENU } from "../actions/types";
const initialState = {
open: true
};
export default function(state = initialState, action) {
switch (action.type) {
case HIDE_MENU:
return {
...state,
open: false
};
case ESTADO_MENU:
console.log("chega aqui");
return {
...state
};
default:
return state;
}
}
but calling it like this:
componentDidMount() {
console.log("Estado do Menu: ", this.props.estadoDoMenu());
}
I get undefined at the console, what is wrong?
I'm pretty new in react so this might be a silly question.
I'm working on an app that manage rss feeds, so the structure of my entire app is similar to this one
<div className="App">
<Header />
<Feeds />
</div>
both components have their own reducer and actions.
the problem appears when I'm trying to create a new feed (actually managed in the feeds reducer) from my header component. so I have to access to the state of the feedsReducer from my headerReducer.
I'm not sure how to proceed at this point.
should I access the feeds reducer from the header component? ( this also implies that the feedsReducer needs to know my header actions)
I'll add some code to make the problem clear
index.js
import feedsReducer from './components/Feeds/FeedsReducer';
import headerReducer from './components/Header/HeaderReducer';
const rootReducer = {
feeds:feedsReducer,
header: headerReducer
};
const store = createStore(combineReducers(rootReducer));
Header/Header.js
import { ADD_FEED } from './actions';
class Header extends Component {
state = {
feedUrl: ""
};
addFeed = () => {
axios.post(
'/feeds/add',
{
url: 'myNewRssFeed.com'
})
.then(feed => {
//this is calling the HeaderReducer
this.props.addFeed(feed.data);
})
.catch(err => console.log(err));
}
}
const mapDispatchToProps = dispatch => {
return {
addFeed: (feed) => dispatch({ type: ADD_FEED, payload: { feed } })
};
};
export default connect(null, mapDispatchToProps)(Header);
Header/actions.js
export const ADD_FEED = "ADD_FEED";
HeaderComponent/HeaderReducer.js
const reducer = (state, action) => {
const newState = {
...state
}
switch (action.type) {
case storeActions.ADD_FEED:
// at this point newState.feeds doesn't exist because it's part from the FeedsReducer
newState.feeds = newState.feeds.push(action.payload.feed);
break;
}
return newState;
}
Feeds/FeedsReducer.js
const initialState = {
feeds: []
}
const reducer = (state = initialState, action) => {
const newState = {
...state
}
switch (action.type) {
//this action is commented because was recently moved to the headerComponent/actions.js
/* case storeActions.ADD_FEED:
newState.feeds = newState.feeds.push(action.payload.feed);
break; */
case storeActions.LOAD_FEEDS:
newState.feeds = action.payload.feeds;
break;
}
return newState;
}
Thanks in advice.
I don't really think you need to access reducer in any way. Reducer function will update store based on action it's listenning to.
Here is an example:
import * as constants from 'constantpathhere';
export function feedReducer(state = INITIAL_STATE, action) {
const { type, payload } = action;
switch(type) {
case constants.ADD_FEED: // listen to ADD_FEED action
return {... state, data: payload };
case constants.LOAD_FEEDS: // listen to LOAD_FEEDS
return {...state, loading: true }
...
default:
return state;
}
}
export function headReducer(state = INITIAL_STATE, action) {
const { type, payload } = action;
switch(type) {
case constants.ANY_ACTION: // listen to ADD_FEED action
return {... state, data: payload };
case constants.ANY_OTHER_ACTION_LOADING: // listen to LOAD_FEEDS
return {...state, loading: true }
...
default:
return state;
}
}
//ACTIONS
export function loadFeeds() {
return {
type: constants.LOAD_FEEDS
}
}
export function addFeed(payload) {
return {
type: constants.ADD_FEED,
payload
}
}
export function triggerAnyAction(payload) {
return {
type: constants.ANY_ACTION,
payload
}
}
These actions above may be dispatched from any component, be it Header or Feeds, only reducer(s) listening to that particular action will update the store.
Briefly, you only need to know which action to dispatch where and only reducer listing to that action will do whatever you instructed it to do
When triggering an action updateLog, it seems it resets other state items. In my case updateLog should manipulate log and that works just fine. The thing is it also resets tasks to the default values. What am I doing wrong here?
Component:
class Generator extends Component {
render() {
return (
<div className="generator">
<Inputs />
<button onClick={this.generate.bind(this)}>Go!</button>
<Log />
</div>
);
}
generate() {
this.props.updateLog("ANYTHING!");
}
}
function mapStateToProps(state) {
return {
tasks: state.tasks
};
}
function matchDispatchToProps(dispatch){
return bindActionCreators({updateLog: updateLog}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(Generator);
Action:
export const updateLog = (message) => {
return {
type: 'LOG_UPDATED',
payload: message
}
};
Logreducer:
const initialLog = "";
export default function (state = initialLog, action) {
switch (action.type) {
case 'LOG_UPDATED':
return state + "\n" + action.payload
break;
}
return state;
}
All reducers:
const allReducers = combineReducers({
tasks: taskReducer,
log: logReducer
});
export default allReducers
taskReducer:
export default function (state = null, action) {
switch (action.type) {
case 'TASK_UPDATED':
var tasks = Object.assign({}, action.payload);
return tasks;
break;
}
// Default task properties
return {
CreateDatabaseTask: {
enabled: false,
type: "sqlite"
}
}
}
The problem lies in your task reducer. If the action type matches none of the ones defined in the switch statement, you should return the current state. Instead, you are returning the initial state.
Try changing it to return the current state instead:
const initialState = {
CreateDatabaseTask: {
enabled: false,
type: "sqlite"
}
}
export default function (state = initialState, action) {
switch (action.type) {
case 'TASK_UPDATED':
var tasks = Object.assign({}, action.payload);
return tasks;
break;
default:
return state;
}
}
I got a problem when do objectAssign to change the state in store into a new data from server, It always get a null as the result.
i call my action in onEnter function(React-Router)
export function GET_SetupTabTitles() {
store.dispatch(getSetupTabTitles());
}
this is my action :
import {
TOGGLE_DRAWER_IN_APPBAR,
GET_SETUP_TAB_TITLES,
} from '../constants/actionTypes';
import axios from 'axios';
const ROOT_URL = 'http://localhost:8000';
export function toggleDrawerInAppBar(open){
return { type: TOGGLE_DRAWER_IN_APPBAR, openStatus: open }
}
export function getSetupTabTitles(){
return function(dispatch){
axios.get(`${ROOT_URL}/api/component/getSetupTabTitles`)
.then(response => {
dispatch({type: GET_SETUP_TAB_TITLES,
payload: response
});
});
}
}
this is my initial state on reducer :
export default {
auth: {
authenticated: (localStorage.getItem('laravel_user_token') !== null),
userinfo: {
name: null
},
error:""
},
comp: {
openDrawerStatus: false,
setupTabTitles: null,
}
};
and this is my reducer :
import {
TOGGLE_DRAWER_IN_APPBAR,
GET_SETUP_TAB_TITLES,
} from '../constants/actionTypes';
import initialState from './initialState';
import objectAssign from 'object-assign';
const compReducer = (state = initialState.comp, action) => {
switch (action.type) {
case TOGGLE_DRAWER_IN_APPBAR:
return objectAssign({}, state, {openDrawerStatus: action.openStatus});
case GET_SETUP_TAB_TITLES:
console.log(action.payload.data);
return objectAssign({}, state, {setupTabTitles: action.payload.data});
default:
return state;
}
};
export default compReducer;
when i do console.log inside
case GET_SETUP_TAB_TITLES:
it show :
Array[2]0: 0:Object 1:Object
On using JSON.stringify() it shows me [{"tabTitle":"Events"},{"tabTitle":"Tasks"}]
but my state (setupTabTitles) didn't change at all.
i do try this one :
case GET_SETUP_TAB_TITLES:
state.setupTabTitles.push(action.payload.data[0]);
return state;
it work, but i don't want to direct change the state.
You don't need to import ojectAssign from 'object-assign'; when you make use of the current ES6 syntax in your code. You only need Object.assign. Also since your action.data.payload is an array and you need to append to an array you can use the spread operator like
return {
...state,
setupTabTitles: [...state.setupTabTitles, action.payload.data]
}
Also you need to initialise you componentState to be an empty array and not null or undefined. Change that to below code
export default {
auth: {
authenticated: (localStorage.getItem('laravel_user_token') !== null),
userinfo: {
name: null
},
error:""
},
comp: {
openDrawerStatus: false,
setupTabTitles: [],
}
};
Try it like below
const compReducer = (state = initialState.comp, action) => {
switch (action.type) {
case TOGGLE_DRAWER_IN_APPBAR:
return Object.assign({}, state, {openDrawerStatus: action.openStatus});
case GET_SETUP_TAB_TITLES:
console.log(action.payload.data);
return {
...state,
setupTabTitles: [...state.setupTabTitles, ...action.payload.data]
}
default:
return state;
}
};
The syntax of objectAssign is different from what I use, you can see it here
var state = {
openDrawerStatus: false,
setupTabTitles: [],
}
var payload = [{"tabTitle":"Events"},{"tabTitle":"Tasks"}]
console.log( {
...state,
setupTabTitles: [...state.setupTabTitles, ...payload]
});
As you are already using ES6, you could just use the object spread operator and get rid of the object-assign library, it would be like this:
import {
TOGGLE_DRAWER_IN_APPBAR,
GET_SETUP_TAB_TITLES,
} from '../constants/actionTypes';
import initialState from './initialState';
const compReducer = (state = initialState.comp, action) => {
switch (action.type) {
case TOGGLE_DRAWER_IN_APPBAR:
return { ...state, openDrawerStatus: action.openStatus };
case GET_SETUP_TAB_TITLES:
return { ...state, setupTabTitles: action.payload.data };
default:
return state;
}
};
export default compReducer;
In your initial state, I would change setupTabTitle from null to an empty array []:
setupTabTitles: [],
And in your reducer, append data to this array:
const compReducer = (state = initialState.comp, action) => {
switch (action.type) {
...
case GET_SETUP_TAB_TITLES:
return {
...state,
setupTabTitles: [
...state.setupTabTitles,
...action.payload.data
]
}
...
}
};
Or if you don't want to append, just replace, I would do:
setupTabTitles: [
...action.payload.data
]
I have a React app built using Redux and Redux-Thunk. Everything works fine, until I try to combine reducers per the Redux docs.
Given an initial, functional reducer
export default function bigReducer(state = { events: [], flash: [] }, action) {
switch (action.type) {
case EVENTS_UPDATED:
return _.extend({}, state, { events: action.pathway_events })
case FLASH_MESSAGE_UPDATED:
return _.extend({}, state, { flash: action.flash })
default:
return state
}
}
When I try to create a composite reducer
function flashReducer(state = { flash: [] }, action) {
switch (action.type) {
case FLASH_MESSAGE_UPDATED:
return _.extend({}, state, { flash: action.flash })
default:
return state
}
}
function eventReducer(state = { events: [] }, action) {
switch (action.type) {
case EVENTS_UPDATED:
return _.extend({}, state, { events: action.pathway_events })
default:
return state
}
}
// either with simple reducer composition
export default function bigReducer(state = {}, action) {
return {
flashReducer: flashReducer(state.flash, action),
eventReducer: eventReducer(state.events, action)
}
}
// or with the combineReducers function
export default const reducer = combineReducers({
flashReducer,
eventReducer
})
the initial state and the reducers seem to get mixed up
// logging the state
var EventListContainer = connect((state) => {
console.log(state)
return { events: state.events })(React.createClass({ ...
// returns the incorrect state
# => Object {flashReducer: Array[0], eventReducer: Array[17]}
How can I combine reducers using React and Redux?
My understanding from the docs is that a named reducer is delegated to handle only that part of the state with the top-level key corresponding to the reducer name. So
const reducer = combineReducers({
flashReducer,
eventReducer
})
implies that you have state like
const state = {
flashReducer: {...},
eventReducer: {...}
}
So you need to a) name your reducers the same as the top-level keys they're supposed to manage, and b) have their default state only represent that subset of the full state object:
function flash(state = [], action) {
switch (action.type) {
case FLASH_MESSAGE_UPDATED:
return action.flash.slice()
default:
return state
}
}
function events(state = [], action) {
switch (action.type) {
case EVENTS_UPDATED:
return action.pathway_events.slice()
default:
return state
}
}
const reducer = combineReducers({
flash,
events
})