I am like in a strange problem. The problem is that I am trying to make an API hit (in service file) which in turn provides some data (it is working), this data is to be updated in my reducer1.js and then returned. Now, my issue is though the value is coming in reducer file, but is not returned, so in turn, state is not changed, and in turn my end component is not rerendered.
Now, when my service file is successfully hitting and then returning data to my reducer1.js, why in the world the updated-state is not returned by "GET_List" action type? Can someone see any problem?
index.js (service file)
const global = {
getActressList: async function(){
const response = await fetch("http://localhost:2000/api/actressList");
const data = await response.json();
return data;
}
}
export default global;
reducer1.js
import global from '../../services/index';
const initialState = {
data: [
{
id: 1,
name: "Aishwarya Rai",
src: "/assets/img/aishwarya.png"
}
]
};
function reducer1(state = initialState, action) {
switch (action.type) {
case "GET_LIST": {
const data = global.getActressList();
data.then((res)=> {
return {
...state,
data: res
}
})
}
default:
return state;
}
}
export default reducer1;
Result:
You are returning from a promise not from a reducer function:
function reducer1(state = initialState, action) {
switch (action.type) {
case "GET_LIST": {
const data = global.getActressList();
data.then((res) => {
// here you are returning from a promise not from a reducer function
return {
...state,
data: res,
};
});
}
default:
return state;
}
}
The code in reducer should be sync like this:
function reducer1(state = initialState, action) {
switch (action.type) {
case "GET_LIST": {
return {
...state,
data: action.payload,
};
}
default:
return state;
}
}
And your data fetching should be moved to component effect like this:
function YourComponent() {
const dispatch = useDispatch();
const data = useSelector(state => state.data)
useEffect(() => {
const data = global.getActressList();
data.then((res) => {
dispatch({type: 'GET_LIST', payload: res});
});
}, [])
...
}
EDIT
If you use class components the fetching logic should be placed in componentDidMount lifecycle hook like this:
class YourComponent extends Component {
state = { data: [] };
componentDidMount() {
const data = global.getActressList();
data.then((res) => {
dispatchYourAction({type: 'GET_LIST', payload: res});
});
}
...
}
Related
I have a useEffect() hook that I'd like to only update when the data in a state-full array changes. I know I can add that array as a dependency, but my issue is the array is not declared in the same file, it's coming from a redux store. Right now I removed the dependency and It works, but the dev tools show me that it is constantly sending new requests to the server when it should send a request only when submitted.
This is the function in the redux store:
/client/src/store/utils/thunkCreators.js
export const fetchConversations = () => async (dispatch) => {
try {
const { data } = await axios.get("/api/conversations");
dispatch(gotConversations(data));
} catch (error) {
console.error(error);
}
};
How do I import the conversations from the redux store as a dependency?
Home.js
useEffect(() => {
fetchConversations();
},[X]);
/client/src/store/utils/conversations.js
const GET_CONVERSATIONS = "GET_CONVERSATIONS";
export const gotConversations = (conversations) => {
return {
type: GET_CONVERSATIONS,
conversations,
};
};
const reducer = (state = [], action) => {
switch (action.type) {
case GET_CONVERSATIONS:
return action.conversations;
case SET_MESSAGE:
return addMessageToStore(state, action.payload);
case ADD_ONLINE_USER: {
return addOnlineUserToStore(state, action.id);
}
case REMOVE_OFFLINE_USER: {
return removeOfflineUserFromStore(state, action.id);
}
case SET_SEARCHED_USERS:
return addSearchedUsersToStore(state, action.users);
case CLEAR_SEARCHED_USERS:
return state.filter((convo) => convo.id);
case ADD_CONVERSATION:
return addNewConvoToStore(
state,
action.payload.recipientId,
action.payload.newMessage
);
default:
return state;
}
};
i started to study the react. here is my problem. I have some reducers
let reducers = combineReducers({
user: userReducer,
index_page: indexReducer,
notifications: notificationsReducer
});
Notifications Reducer has its own state of notifications for their renderingand in indexReducer there is. An axios request which, after the response, should draw a notification to the user - change state in notificationsReducer.
I do not quite understand how to do this.
This is my code:
notificationsReducer
let initialState = [
{id: 3, text: 'test_msg', state: 'error'}
];
export const createNotificationActionCreator = (msg_text, msg_state) => {
return {
type: 'SHOW_NOTIFY',
msg_text: msg_text,
msg_state: msg_state
}
}
const notificationsReducer = (state = initialState, action) => {
switch (action.type) {
case SHOW_NOTIFY:
let msg = {
text: action.msg_text,
msg_state: action.msg_state
};
state.push(msg);
break;
}
return state;
}
indexReducer
const indexReducer = (state = initialState, action) => {
switch (action.type) {
case CREATE_NEW_BET:
let bet_data = new Object();
bet_data.bet = state.betAmount;
bet_data.color = action.color;
axios.get('http://localhost/createbet', {
params: {
bet_data
}
}).then(function (response) {
// CHANGE STATE IN notificationsReducer
});
break;
}
return state;
}
To update state in another reducer, I would suggest dispatching the SHOW_NOTIFY action right after dispatching the CREATE_NEW_BET. This can be done using Redux Thunks.
Also read this Stack Overflow answer on suggestions to update state managed by another reducer: Updating state managed by another reducer
With redux-thunk setup, this is what your thunk would look like:
const createBetAndNotify = () => (dispatch) => {
return dispatch({ type: "CREATE_NEW_BET" }).then(() => {
dispatch({ type: "SHOW_NOTIFY" })
})
}
Then inside your React component, you would dispatch the above thunk:
dispatch(createBetAndNotify());
Dispatch multiple actions in redux-thunk and receive infinite loop.
I am trying to turn on a spinner before a request goes to the back-end and stop spinner after request succeeds or fails.
Does anyone have any idea about where I've done mistake?
My code looks like this.
logic.js
import * as actions from "./actions";
export const getData = () => {
return dispatch => {
dispatch(actions.startSpinner());
setAuthorizationToken(getToken());
axiosInstance
.get("/data")
.then(response => {
dispatch(actions.stopSpinner()); //I guess this is problem ?
dispatch(actions.getData(response.data));
})
.catch(err => {
console.log(err);
dispatch(actions.stopSpinner());
});
};
};
And file actions.js
export const startSpinner = () => {
return { type: actionTypes.START_SPINNER };
};
export const stopSpinner = () => {
return { type: actionTypes.STOP_SPINNER };
};
export const getData = data => {
return {
type: actionTypes.GET_DATA,
payload: data
};
};
And reducer for it spinner.js
import actionTypes from "../actionTypes";
export default (state = false, action) => {
switch (action.type) {
case actionTypes.START_SPINNER: {
return (state = true);
}
case actionTypes.STOP_SPINNER: {
return (state = false);
}
default: {
return state;
}
}
};
And reducer for data dataReducer.js
import actionTypes from "../actionTypes";
const defaultState = [];
export default (state = defaultState, action) => {
switch (action.type) {
case actionTypes.GET_DATA: {
let newState = [...action.payload];
return newState;
}
default: {
return state;
}
}
};
I've run into an odd issue, where my redux store seems to be returning a duplicate of a different value? (Still learning terms so sorry if I mixed them up!)
I have 2 states. Users, and Added. I want to show to lists, one using the data from each one of them. currently, fetchUsers works fine, but fetchAdded shows Users for an unknown reason so both lists show the same data.
If I switch fetchUsers to use refAdded then it shows Added, so now it only shows the added array in both lists. I figured that means the actual calls are working cause it can get the data from Firebase, but I don't know why this would happen.
FetchUsers which gets a list of users from firebase looks like this:
export function fetchUsers() {
return (dispatch) => {
refUsers.on('value', snapshot => {
dispatch({
type: 'FETCH_USER',
payload: snapshot.val()
});
});
}
}
FetchAdded looks like this:
export function fetchAdded() {
return (dispatch) => {
refAdded.on('value', snapshot => {
dispatch({
type: 'FETCH_ADDED',
payload: snapshot.val()
});
});
}
}
The reducers look like this:
export default function(state = [], action) {
switch (action.type) {
case 'FETCH_USER':
return [action.payload];
case 'ADDED_USER':
return [action.payload, ...state];
case 'MOVE_USER':
const newState = [...state];
newState.splice(action.payload.index, 1);
return newState;
case 'MOVE_ITEM':
return [action.payload.user, ...state];
default:
return state
}
}
and fetch Added is:
export default function(state = [], action) {
switch (action.type) {
case 'FETCH_ADDED':
return [action.payload];
case 'MOVE_ITEM':
const newState = [...state];
newState.splice(action.payload.index, 1);
return newState;
case 'MOVE_USER':
return [action.payload.user, ...state]
default:
return state
}
}
I combine them both here:
const rootReducer = combineReducers({
users: UserReducer,
added: AddedReducer
});
and my firebase client exporting looks like this:
firebase.initializeApp(config);
export const refUsers = firebase.database().ref("users")
export const refAdded = firebase.database().ref("added")
export const auth = firebase.auth
export const provider = new firebase.auth.GoogleAuthProvider();
In my actual page where I display the 2 lists, this is what I have:
function mapStateToProps(state) {
return {
users: state.users,
added: state.added
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ addUser, moveUser, moveItem, fetchUsers, fetchAdded }, dispatch);
}
In my react/redux application have the following actions
export function getSeatingChartConfiguration(team) {
return function(dispatch) {
ref.child(team.key).once('value').then(function(snapshot) {
dispatch(loadSeatingChart(snapshot.val()));
});
};
}
export function saveSeatingChartSection(key, sectionData){
return function(dispatch) {
ref.child(key).once('value').then(function(snapshot) {
let data = snapshot.val();
let sections = data.sections;
let index = snapshot.val().sections.map( (el) => el.name).indexOf(sectionData.name);
if(index !== -1) {
sections[index] = sectionData;
} else {
sections.push(sectionData);
}
data.sections = sections;
ref.child(key).update(data, function(error) {
dispatch(loadSeatingChart(data));
});
});
};
}
Here is the reducer
export default function seatingChart(state = {}, action) {
switch(action.type) {
case actionTypes.LOAD_SEATING_CHART_CONFIGURATION:
return action.seatingChartConfiguration;
default:
return state;
}
}
I am not getting any errors when getSeatingChartConfiguration() is called but I have receiving Error: A state mutation was detected between dispatches, in the pathseatingChart.sections.2.points.What do I need to change in my action or reducer to not mutate the state.
Use Object.assign with initial state to avoid state mutation like below,
const initialState = { seatingChartConfiguration: [] }
export default function seatingChart(state = initialState, action) {
switch(action.type) {
case actionTypes.LOAD_SEATING_CHART_CONFIGURATION:
return Object.assign({}, state, { seatingChartConfiguration: action.seatingChartConfiguration });
default:
return state;
}
}