Can log updated value but cant render it with mobx flow - react-native

I am trying to make a really simple api call without any logic at all.Althoough I get an illegible object in the consoel called 'proxy' at leaset (not expected either) I cant return anything in the render() method and it throws a typeError.
my code:
Store:
import {observable, configure, action,flow, computed, decorate, set, runInAction} from 'mobx';
import {observer, inject} from 'mobx-react'
configure({enforceActions:'observed'})
class GenStore {
verseData = []
state = "pending"
getVerseData = flow(function*() {
this.verseData = []
this.state = "pending"
try {
const response = yield fetch('https://api.quranwbw.com/2/10')
const data = response.json()
this.state = "done"
this.verseData = data
} catch (error) {
this.state = "error"
}
})
}
decorate(GenStore, {state:observable, verseData: observable, getVerseData:action})
export default new GenStore()
Retrieval:
import {observable, configure, action,flow, computed, decorate, set, runInAction} from 'mobx';
import { computedFn } from "mobx-utils"
import {observer, inject} from 'mobx-react'
import React from 'react'
import GenStore from './GenStore'
class Show extends React.Component{
componentDidMount(){
this.props.GenStore.getVerseData()
}
render(){
console.log(this.props.GenStore.verseData)
return <h1>{this.props.GenStore.verseData.words[0].word_arabic}</h1>
}
}
export default inject('GenStore')(observer(Show))
error returned when i try to render:
TypeError: Cannot read property '0' of undefined
Any help would be appreciated.
Thanx in advance.
Oh, and if you have any suggestion as to how to implement this call if you think flow isnt the choice method, please advise me and tell me how i can do it best

Because getVerseData is async function and when component renders for the first time verseData is an empty array (why it is empty array though? It should be empty object) and respectively verseData.words is undefined.
You can do several things to deal with it, for example, check if verseData.words exists and if not show some loader component instead.

Related

react-redux useSelector() hook not working

I am new to React Native Programming. So, please tell me in detail. thank you.
calling use Selector
I am calling use Selector inside my functional component like this:
import { useDispatch, useSelector } from 'react-redux';
const AddAddressScreen = ({ navigation }) => {
const dispatch = useDispatch();
const data = useSelector(state => state);
console.log(data + "happy Coding");
return (
<View style={styles.container}>
<View>
);
}
export default AddAddressScreen;
My reducer looks like this
case types.API_LOGIN_SUCCESS:
if (action.result.result.mobile_verified === false) {
return {
...state,
onLoad: false,
result: action.result,
status: action.status,
error: null,
navigation: action.navigation.navigate("VerifyMNO")
};
} else {
return {
...state,
onLoad: false,
result: action.result,
status: action.status,
error: null,
navigation: action.navigation.navigate("AddAddress")
};
}
here my mobile number is verified so I move to the address screen.
where I use Use Selector which gives me an error. while I remove above two lines my code runs successfully.
My saga looks like this
export function* watchLoginUserInfo() {
yield takeLatest(types.LOGIN_USER, loginApiSaga)
}
My root saga
import { all, fork } from 'redux-saga/effects';
import { watchLoginUserInfo, } from './authenticationSagas';
function* rootSaga() {
yield all([
watchLoginUserInfo(),
])
}
export default rootSaga;
My Store looks like this
import {createStore, applyMiddleware} from 'redux';
import rootReducer from '../redux/reducers/root-reducer.js'
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../redux/sagas/rootSaga';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
export {store};
when ever I use use Selector hook in my code it gives me the following error.
error 1
error 2, 3, 4
Use the select effect from redux-saga inside of a reducer: https://redux-saga.js.org/docs/api/#selectselector-args
For example const selectedState = yield select(state => state);.
The useSelector hook is for use inside of a function component.
EDIT: since the above doesn't seem to be the issue, I think the issue is that you're calling navigation functions from within your reducer. Reducer code can have no side effects, so you can't call navigation.navigate(...) from within the reducer. This will need to happen in the saga code instead. It might be able to be done in the loginApiSaga or in a dedicated saga that is triggered by API_LOGIN_SUCCESS.

React Native: Function Undefined

I am building an app in react native and keep getting an error with a function being defined and cannot seem to figure out how to fix it.
This is the portion of the code that causes the error. It is meant to fetch data from an api using redux and redux-thunk to do it.
componentDidMount() {
this.props.fetchData(
V.sprintf(
"http://timetree.igem.temple.edu/api/pairwise/%s/%s",
this.state.taxonA,
this.state.taxonB
)
);
}
here is the fetchData function that the component uses it is located in an actions folder.
export const fetchData = url => {
return async dispatch => {
dispatch(fetchingRequest());
try {
let response = await fetch(url);
let json = response.json();
dispatch(fetchingSuccess(json));
} catch(error){
dispatch(fetchingFailure(error));
}
}
};
here is how I try to pass it to the component
SearchScreen.propTypes = {
fetchData: PropTypes.func.isRequired,
response: PropTypes.object.isRequired
};
So as I said in comments with propTypes you're not passing anything, you're testing the type of your props. You should import your function and pass it to connect.
import { connect } from 'react-redux';
import { fetchData } from '../actions';
export default connect(mapStateToProps, { fetchData })(App);
the propType define only the types, not the actual value.
if you export the function
export const fetchData
just import it from your component file, and use it:
fetchData(
V.sprintf(
"http://timetree.igem.temple.edu/api/pairwise/%s/%s",
this.state.taxonA,
this.state.taxonB
)
);
without "this.props".

Reflux Action Not Reaching Method

I have build a store with actions like below:
import Reflux from 'reflux'
export const AuthActions = Reflux.createActions(['updateAuth', 'otherThing'])
export class AuthStore extends Reflux.Store
{
constructor()
{
super()
this.state = {
authToken: null,
authUser: null
}
this.listenables = AuthActions
}
otherThing()
{
debugger
console.log("OTHER THINNGGS")
}
updateAuth(token, user)
{
debugger
console.log("DODOODO")
this.setState({authToken: token, authUser: user})
}
}
However, anytime I import AuthActions and call AuthActions.otherThing() or AuthActions.updateAuth(token, user) I never reach those debuggers and nothing is printed to console, as if the methods are never called. I have tried renaming to onUpdateAuth and onOtherThing as well with no change.
Turns out, my store was never initialized. When you have a store, you have to either import and set the store in your component or initialize elsewhere in order to ensure the listenables are linked up to an instance of the store. In order to ensure my stores are always available, I create a single instance of the stores and exported that instead of the class itself.

REDUX: understanding a bit the concept + react native

So, I am working on a pretty straight forward mobile app that has these scenes:
a list of people
person profile
add form
now, what I do, when I first load the LIST scene, I make an API call (I have a list component that I populate once I get results from the API... state.people).
All good here... when I tap on a person he's profile opens, no extra API calls, just passing the person object from state.people array.
All good here as well.
When I open ADD NEW person and send the form I make another API call (I post the information and get the new Object back)...
now the bit that is confusing to me.
What I would like is to update the LIST scene state.people by making another API call (get all again) after I get the OK confirmation from the POST.
and then navigate to Person's profile.
but, I am outside the scope of the LIST scene (I am in ADD NEW form). So, what would be the correct redux logic for this one?
The LIST component is already mounted... how do I communicate to LIST if I am on different scene
all these binding actions to components properties is confusing too... why can't redux act like a global hub that would always be accessible and would always retain it's state (at least on mobile app)
There is really a lack of real app examples... so far I see only very simplified examples that are not very useful on the grand scale to understand the whole flow
the store I have
/**
* STORE
*/
'use strict';
import { createStore, applyMiddleware } from 'redux';
import reducer from './_reducer';
import promiseMiddleware from 'redux-promise-middleware';
import thunkMiddleware from 'redux-thunk';
const store = createStore(reducer, {}, applyMiddleware(
thunkMiddleware,
promiseMiddleware()
));
export default store;
and the actions I have:
import * as constants from '../../constants/constants';
import request from '../../utils/request';
export const getAll = () => ({
type: constants.PEOPLE_FETCH,
payload: request(constants.API_PATH + 'person', {method: 'GET'})
});
export const search = (data, searchTerm) => ({
type: constants.PEOPLE_SEARCH,
payload: _filter(data, searchTerm)
});
export const save = (data) => ({
type: constants.PERSON_SAVE,
payload: request(constants.API_PATH + 'person', {method: 'POST', body: JSON.stringify(data)})
});
This can be an example architecture for your app:
Make a Redux store with list of people.
On initial API call, update the store to contain the list fetched by API call.
Wrap your app inside Provider and pass the store to the Provider.
Use connect and mapStateToProps and mapDispatchToProps to connect the Redux store to React state.
Whenever you update or insert new person, and get the new object, you need to dispatch an action which then goes to the reducer function which finally returns the updated Redux store, and dont worry with the re-rendering as React does the re-rendering itself whenever there is a change in a state.
I'll give a small example of store/actions/reducer, with a react + redux app.
store.js
import { applyMiddleware, compose, createStore } from 'redux'
import reducer from './reducer'
import logger from 'redux-logger'
// TOOD: add middleware
let finalCreateStore = compose(
applyMiddleware(logger())
)(createStore)
export default function configureStore (initialState = { todos: [] }) {
return finalCreateStore(reducer, initialState)
}
actions.js
let actions = {
helloWorld: function(data){
return {
type: 'HELLO_WORLD',
data: data
}
}
};
export default actions
reducer.js // Please read from Redux docs that reducers need to be pure functions
export default function myReducer(state = [], action) {
switch (action.type) {
case 'HELLO_WORLD':
return 'welcome' + data;
default:
return state;
}
}
Component.js (the React App) //In component whenever you receive new object, dispatch an action which will modify the store.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import actions from '../redux/actions'
class App extends Component {
handleClick() {
store.dispath(action.helloWorld("jimmy")); //this dispatches an action, which goes to the reducer to change the state and adds 'welcome' before 'jimmy'
}
render() {
return (
<div onClick={this.handleClick.bind(this)}>
{store.getState()} //getState function to access store values
</div>
)
}
}
function mapStateToProps(state) {
return state
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch) //binds all the actions with dispatcher and returns them
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
This works like whenever you click the 'div' in the React Component, it calls the function, handleClick(), in which there is an action dispatch. This action then calls the reducer itself to update the store. I know you might get confused that how is store getting updated. Its a bit confusing but for that you need to follow a basic tutorial to explain React+Redux.
Please note this is not a runnable example, just a pseudocode. I recommend you to watch this youtube series to completely understand the redux stores+ react+webpack

Redirect to login page when not logged in causes state transition error

I am struggling to figure out how to correctly redirect to a login page when the user is not logged in using React and Redux.
Currently, in the constructor of the component, I check to see if the username is set, and if not, I use the routeActions provided by redux-simple-router to redirect to the login page. However, I get this error:
Warning: setState(...): Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
I understand that setting the state inside of the render function should be avoided. but I am not sure where I should detect and redirect. I have also tried checking the auth state in the componentWillReceiveProps and ComponentWillMount, but no luck.
// WordListContainer.js
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {routeActions} from 'redux-simple-router';
import WordList from '../components/Words/WordList';
import {addWord, editWord, deleteWord, fetchWords} from '../actions/words';
function mapStateToProps(state) {
return {
auth: state.auth,
words: state.words
};
}
function mapDispatchToProps(dispatch) {
return {
router: bindActionCreators(routeActions, dispatch),
actions: bindActionCreators({
addWord, editWord, deleteWord, fetchWords
}, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(WordList);
and
// WordList.js
import React, {Component} from 'react';
import {Link} from 'react-router';
import WordListItem from './WordListItem';
export default class WordList extends Component {
constructor(props) {
super(props);
if(!this.props.auth.username) {
// This redirection causes the error
this.props.router.push('/login');
}
}
render() {
...
}
}
Is there a good place where I can check the state and redirect before even trying to render the component? Perhaps somehow using the Container Object, though I am not quite sure how to do it where I have access to both state and dispatch.
try componentDidUpdate() as this lifecycle method will always be called whenever the state changes.