React Native Redux: props undefined - javascript

How to call a callback function?
Or how to set state after mapStateToProps get data?
RegistrationContainers:
function mapStateToProps(state, props) {
return {
isRegistered: state.user.registered // isRegistered: 1
};
function mapDispatchToProps(dispatch, props) {
return {
registration:(data) => dispatch(registration(data))
};
RegistrationComponents:
onRegister(){
let email = this.state.email;
let password = this.state.password;
let registerData = {email:email,password:password};
registerData = JSON.stringify(registerData);
this.props.registration(registerData)
this.setState({isRegistered: this.props.isRegistered}) // isRegistered: undefined, but second run after is correct
registration actions:
export function OnRegistration(registered) {
/* ... */
return registered }
export function registration(regData) {
return (dispatch) => {
sendDataToApi(regData, "registration",(isRegistered)=>{
dispatch({
type: ON_REGISTRATION,
registered: isRegistered
})
})
}
}
registration reducer:
const initialState = {};
export function loginReducer(state = initialState, action) {
switch(action.type){
case REGISTRATION:
return Object.assign({},state,{
registered: action.registered
})
}
return state; }
Containers get current state, but containers props can't reach from components.

modify your methods by adding callback() functions. i.e.
function mapDispatchToProps(dispatch, props) {
return {
registration:(data, cb = null) => dispatch(registration(data, cb))
};
and modify registration(data) method to:
registration(data, cb) {
/* your code here */
/* on successful registration, set user.registered to true in redux and
then call cb function */
cb && cb();
}
modify onRegister() method to:
onRegister(){
let email = this.state.email;
let password = this.state.password;
let registerData = {email:email,password:password};
registerData = JSON.stringify(registerData);
this.props.registration(registerData, () => {
this.setState({isRegistered: this.props.isRegistered})
}
})

Related

Why are my Action creators not dispatching data to redux store in Next js?

I just started using NextJS for the first time and I'm trying to use it with Redux.
I have been able to set up the redux and set up the getServerSideProps function in index.js.
The problem however is that the store returns an empty state even after I have fetched the data. When I console.log the fetched data from inside the getServerSideProps function, I see it in my terminal, so I'm left to suspect that the problem is that my action creator is not dispatching the data to the store. I even tried to handle the data fetching and dispatch from inside the action creator using redux-thunk, but that also did not work. Please I really need some help with this.
My action creator:
export const initializeArticles = (articles) => {
return {
type: 'INITARTICLES',
data: articles
}
}
My reducer:
const articlesReducer = (state = initialState, action) => {
switch (action.type) {
case 'INITARTICLES':
return action.data
case 'ADDARTICLE':
return state.concat(action.data)
case 'INCREMENTARTICLELIKES':
return state.map((article) => {
if(article.id === action.id) {
return {
...article,
likes: article.likes + 1
}
} else {
return article
}
})
case 'DELETEARTICLE':
return state.filter(article => article.id !== action.id)
case 'ADDCOMMENT':
return state.map((article) => {
if (article.id === action.id) {
return {
...article,
comments: article.comments.concat(action.data)
}
} else {
return article
}
})
case 'APPROVECOMMENT':
return state.map((article) => {
if (article.id === action.articleId) {
return article.commentIsApproved = true
} else {
return article.commentIsApproved = false
}
})
default:
return state
}
}
export default articlesReducer
My store setup:
let store;
const initstore = (initialState) => {
return createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(thunk))
)
}
export const initializeStore = (preloadedState) => {
let _store = store ?? initstore(preloadedState)
if (preloadedState && store) {
_store = initstore({
...store.getState(),
...preloadedState
})
store = undefined
}
if(typeof window === 'undefined') return _store
if(!store) store = _store
return _store
}
export function useStore(initialState) {
const store = useMemo(() => initializeStore(initialState), [initialState])
return store
}
My getServerSideProps function:
export const getServerSideProps = () => {
const reduxStore = initializeStore()
const { dispatch } = reduxStore
getPosts().then((posts) => {
console.log(posts)
dispatch(initializeArticles(posts))
})
return { props: { initialReduxState: reduxStore.getState() } }
}
export default Home

Get the EditorState(DraftJS) from DB with Redux

I want to repopulate the editor's state with the values i have saved to Firebase.
OnSubmit:
sendNotes = (e) => {
e.preventDefault();
let contentState = this.state.editorState.getCurrentContent()
let note = { content: convertToRaw(contentState) }
note["content"] = JSON.stringify(note.content);
this.props.createNote(note.content);
};
NoteAction:
export function getNote() {
return (dispatch) => {
database.on("value", (snapshot) => {
dispatch({
type: LOAD_NOTE,
payload: snapshot.val(),
});
});
};
}
noteReducer:
export default function (state = {}, action) {
switch (action.type) {
case LOAD_NOTE:
return action.payload;
default:
return state;
}
}
REDUX/firebase:
{
type: 'LOAD_NOTE',
payload: {
'-MNOwBIWNqY_ZFDO4ILs': '{"blocks":[{"key":"c27el","text":"ASD!!!!!!!!!!!!","type":"unstyled","depth":0,"inlineStyleRanges":[],"entityRanges":[],"data":{}}],"entityMap":{}}',
'-MNOyHLvaORxEmuuJmzJ': '{"blocks":[{"key":"c27el","text":"HELLO WORLD","type":"unstyled","depth":0,"inlineStyleRanges":[],"entityRanges":[],"data":{}}],"entityMap":{}}',
'-MNOyP50oGHRLiP3T5_h': '{"blocks":[{"key":"c27el","text":"This is a REDUX STORE","type":"unstyled","depth":0,"inlineStyleRanges":[],"entityRanges":[],"data":{}}],"entityMap":{}}'
}
}
MapStateToProps:
function mapStateToProps(state, ownProps) {
return {
note: state.notes,
};
}
export default connect(mapStateToProps, { getNote })(TextEditor);
My Code:
componentDidMount() {
this.props.getNote();
}
componentWillReceiveProps = (nextProps) => {
if (nextProps.note !== null) {
let item = "";
_.map(nextProps.note, (note, key) => {
return (item = note);
});
this.setState({
editorState: EditorState.createWithContent(convertFromRaw(JSON.parse(item))),
});
}
};
The code is working but I'm not 100% sure about these lifecycle methods & if the code i have written is 'stable'. I am stuck with this, please :).

Data gets overwritten in the redux-reducer

I was trying to implement a page-by-page onboarding signup screen for which the first page collects users horoscopic sign and in the next page, it asks for name. The thing is the sign_id gets replaced by name. Please check the codes below
action.js
import * as types from './types';
export function addNewUserRequest(values) {
console.log('action data', values);
return {
type: types.NEW_USER_REQ,
values,
};
}
reducer.js
import createReducer from '../lib/createReducer';
import * as types from '../actions/types';
const initialState = {
values: [],
};
export const newUserReducer = createReducer(initialState, {
[types.NEW_USER_REQ](state, action) {
console.table('reducer action test', state, action.values);
return {
...state,
values: action.values,
};
},
createreducer.js
export default function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action);
} else {
return state;
}
};
}
Page1.js
const dispatch = useDispatch();
const onPress = (val) => {
console.log('SELECTED SIGN', val);
let value = {
sign_id: val,
};
NavigationService.navigate('Login3');
dispatch(newUserActions.addNewUserRequest(value));
};
Page2.js
const dispatch = useDispatch();
const handlePress = () => {
let value = {
name: userName,
};
dispatch(newUserActions.addNewUserRequest(value));
NavigationService.navigate('Login4');
};
Console
Change param in addNewUserRequest from values to value as only single value is passed. Then append action.value to state.values.
export function addNewUserRequest(value) {
console.log('action data', value);
return {
type: types.NEW_USER_REQ,
value,
};
}
export const newUserReducer = createReducer(initialState, {
[types.NEW_USER_REQ](state, action) {
console.table('reducer action test', state, action.value);
return {
...state,
values: { ...state.values, ...action.value }
};
},

How to change the internal state property of a component in an action creator function

I am working on a React application and I am using Redux to store the state. I have the following code:
category-arrows.component.jsx:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { increaseCategoryRank, decreaseCategoryRank, fetchCategoryRanks } from '../../redux/menu/menu.actions';
import './category-arrows.styles.scss';
class CategoryArrows extends Component {
state = {
isSending: false
}
render() {
const { category } = this.props;
const categoryClicked = true;
return (
<div className="arrows-container">
<div className="up-arrow" onClick={
() => {
if(this.state.isSending === false) {
this.props.increaseCategoryRank(category, categoryClicked)
}
this.props.fetchCategoryRanks(this.props.menu);
}}></div>
<div className="category-rank">
<p>{category.rank}</p>
</div>
<div className="down-arrow" onClick={
() => {
if(this.state.isSending === false) {
this.props.decreaseCategoryRank(category, categoryClicked)
}
this.props.fetchCategoryRanks(this.props.menu);
}}></div>
</div>
)
}
}
const mapStateToProps = state => ({
menu: state.menu
})
export default connect(mapStateToProps, { increaseCategoryRank, decreaseCategoryRank, fetchCategoryRanks } )(CategoryArrows);
menu.actions.js:
import { apiUrl, apiConfig } from '../../util/api';
import { INCREASE_CATEGORY_RANK, DECREASE_CATEGORY_RANK, FETCH_CATEGORY_RANKS } from './menu.types';
export const decreaseCategoryRank = (category, categoryClicked) => dispatch => {
dispatch({ type: DECREASE_CATEGORY_RANK, category, categoryClicked })
}
export const increaseCategoryRank = (category, categoryClicked) => dispatch => {
dispatch({ type: INCREASE_CATEGORY_RANK, category, categoryClicked })
}
export const fetchCategoryRanks = menu => async dispatch => {
console.log("Printing menu (fetch category ranks)");
console.log(menu);
var sentRequests = 0;
menu.map(async (category) => {
const menuLength = menu.length;
const options = {
...apiConfig(),
method: 'PUT',
body: JSON.stringify(category)
}
const response = await fetch(`${apiUrl}/category/${category._id}`, options)
let data = await response.json()
if (response.ok) {
console.log("It got sent")
sentRequests++;
console.log("Printing sentRequests");
console.log(sentRequests);
if(sentRequests === menuLength) {
console.log("All the requests have been sent");
}
} else {
alert(data.error)
}
});
dispatch({ type: FETCH_CATEGORY_RANKS, menu });
}
menu.types.js:
export const INCREASE_CATEGORY_RANK = "INCREASE_CATEGORY_RANK";
export const DECREASE_CATEGORY_RANK = "DECREASE_CATEGORY_RANK";
export const FETCH_CATEGORY_RANKS = "FETCH_CATEGORY_RANKS";
menu.reducer.js:
// import INITIAL_STATE from './menu.data';
import { INCREASE_CATEGORY_RANK, DECREASE_CATEGORY_RANK, FETCH_CATEGORY_RANKS } from './menu.types';
const INITIAL_STATE = []
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case INCREASE_CATEGORY_RANK: {
console.log("Went into increase category rank");
if(action.categoryClicked === false) {
return state;
}
const menuArray = [...state];
var index = menuArray.map(category => category._id).indexOf(action.category._id);
//if it's the first element in array it won't move up
if(index === 0) {
return state;
} else {
const temp = menuArray[index];
menuArray[index] = menuArray[index - 1];
menuArray[index - 1] = temp;
var newrank = 0;
menuArray.forEach(category => {
category.rank = newrank++;
});
return menuArray;
}
}
case DECREASE_CATEGORY_RANK: {
console.log("Went into decrease category rank");
if(action.categoryClicked === false) {
return state;
}
const menuArray = [...state];
console.log(menuArray);
var index = menuArray.map(category => category._id).indexOf(action.category._id);
//if it's the last element in the array, it won't move down
if(index === menuArray.length - 1) {
return state;
} else {
const temp = menuArray[index];
menuArray[index] = menuArray[index + 1];
menuArray[index + 1] = temp;
var newrank = 0;
menuArray.forEach(category => {
category.rank = newrank++;
});
return menuArray;
}
}
case FETCH_CATEGORY_RANKS:
return state;
default:
return state;
}
}
In my CategoryArrows component I have a state property called isSending which is set to false. In my fetchCategoryRanks action creator, I am sending information about categories from the menu array in the state to a server using fetch.
I would like to be able to set the isSending property from the CategoryArrows component to true or false, depending on certain conditions in the function fetchCategoryRanks.
However, I am not sure what the best way to do this is. Any insights are appreciated.
First way
You can change internal state by sending a callback function to your axios api call. Before the axios request starts you can call that callback function from axios api function to set isSending=true and after request completed again call callback function to set isSending=false. Callback function implementation must be in component from where you are calling axios api.
Api call
this.props.fetchCategoryRanks(this.props.menu, (response) => {
if (isRequestStart) {
this.setState({
isSending: true
});
}
if (!isRequestStart) {
this.setState({
isSending: false
});
}
});
Below is your fetch request
export const fetchCategoryRanks = (menu, callback) => async dispatch => {
var sentRequests = 0;
menu.map(async (category) => {
const menuLength = menu.length;
callback({
isRequestStart: true
});
const options = {
...apiConfig(),
method: 'PUT',
body: JSON.stringify(category)
}
const response = await
fetch(`${apiUrl}/category/${category._id}`, options)
let data = await response.json()
if (response.ok) {
callback({
isRequestStart: false
});
console.log("It got sent")
sentRequests++;
console.log("Printing sentRequests");
console.log(sentRequests);
if (sentRequests === menuLength) {
console.log("All the requests have been sent");
}
} else {
alert(data.error);
callback({
isRequestStart: false
});
}
});
dispatch({
type: FETCH_CATEGORY_RANKS,
menu
});
}
Second way
You can use a reducer where you can set initial state of isSending by dispatching a function from the axios api, calling the dispatch function same as above. And you can use that reducer state into your component.

Redux connected React component not updating on state change

Whenever my 'COLLEGE_ADDED' action is dispatched I can see the state changes in the reducer. However the update related lifecycle methods on the CollegeSearchList component and it's children aren't being called. These components aren't re-rendering presumably because of this.
I have read the docs about not mutating state and I don't think I am. Complete code can be found here https://github.com/tlatkinson/react-search-widget.
components/search/college/CollegeSearchList.js
class CollegeSearchList extends Component {
componentWillUpdate (nextProps, nextState) {
console.log(nextProps.searchItems);
console.log(nextState);
return true;
}
render () {
return (
<SearchList searchItems={this.props.searchItems} SearchListItem={CollegeSearchListItem} />
)
}
}
const mapStateToProps = (state, {id}) => {
return {
searchItems: getSearchResultsById(state.searchState, id),
SearchListItem: CollegeSearchListItem,
}
};
CollegeSearchList = connect(
mapStateToProps
)(CollegeSearchList);
reducers/search.js
const searchReducer = (searchState = [], action) => {
switch(action.type) {
case 'COLLEGE_SEARCH':
return mergeData(searchState, action, 'college', 'phrase');
case 'COLLEGE_SEARCH_SUCCESS':
return mergeData(searchState, action, 'college', 'searchResults');
case 'COLLEGE_ADDED':
return updateCollegeAdded(searchState, action.collegeId, true);
case 'COLLEGE_REMOVED':
return updateCollegeAdded(searchState, action.collegeId, false);
default:
return searchState;
}
};
export default searchReducer
const updateCollegeAdded = (searchState, collegeId, added) => {
const newState = {...searchState};
for (let id of Object.keys(newState)) {
const searchComponent = searchState[id];
if(searchComponent.searchType === 'college') {
searchComponent.searchResults.forEach(searchResult => {
if(searchResult.id === collegeId) {
searchResult.added = added;
}
});
}
}
return newState;
};
const mergeData = (data, action, searchType, propertyModified) => {
return {
...data,
[action.id]: {
searchType,
...data[action.id],
[propertyModified]: action[propertyModified],
}
};
};
actions/index.js
export const addRemoveCollege = (collegeId, collegeName, addToList) => (dispatch) => {
if (addToList) {
api.addToCollegeList(collegeId)
.then(() => {
dispatch({
type: 'COLLEGE_ADDED',
collegeId,
collegeName,
});
})
} else {
api.removeFromCollegeList(collegeId)
.then(() => {
dispatch({
type: 'COLLEGE_REMOVED',
collegeId,
collegeName,
});
})
}
};

Categories

Resources