Redux-Thunk unable to get props in component - react-native

I have an application with firebase connected. I am able to at each step console log and get see the data from firebase being passed around however when im in the component level it always returns empty.
import * as firebase from "firebase";
import { GET_LIST } from './types';
export const getListThunk = () => {
return (dispatch) => {
const teams = [];
const teamsObj = {};
var that = this;
var ref = firebase.database().ref('SignUp/' + "577545cf-c266-4b2e-9a7d-d24e7f8e23a5");
//var query = ref.orderByChild("uuid");
console.log("uuid thunk ");
ref.on('value', function (snapshot) {
console.log("snap ", snapshot.val())
snapshot.forEach(function (child) {
let currentlike = child.val()
console.log("schedas ", currentlike)
teams.push(currentlike);
console.log("teams ",teams);
});
dispatch({ type: GET_LIST, payload: teams})
})
}
}
At all the console log here I am able to receive the information from firebase. The console displays:
Now I checked my reducer to see if I can see my information there.
import { GET_LIST } from '../actions/types';
const INITIAL_STATE = {
jello: 'hello'
};
const listReducer = (state = INITIAL_STATE, action) => {
switch (action.type){
case GET_LIST:
console.log("action ", action.payload);
return action.payload;
default:
console.log("default ");
return state;
}
};
export default listReducer;
This console log labeled action shows the payload as well
So I am again able to see the data in the reducer payload.
Now checking my component I would assume calling this.props would show the data again however it shows up empty.
Component:
mport React, { Component } from "react";
import {
View,
StyleSheet,
Button,
SafeAreaView,
ScrollView,
Image,
TouchableOpacity,
Alert,
Animated,
FlatList
} from "react-native";
import {connect} from 'react-redux';
import { getListThunk } from '../actions';
import Form from '../components/Form';
import firebase from "firebase";
import * as theme from '../theme';
import Block from '../components/Block';
import Text from '../components/Text';
import App from "../../App";
class RenderRequests extends Component {
constructor(props) {
super(props);
this.params = this.props;
uuid2 = this.props.uuid;
}
componentWillMount(){
this.props.getListThunk();
}
componentDidMount(){
console.log("did mount " , this.params.uuid)
var uuid = this.params.uuid;
//this.props.getListThunk({uuid});
}
render() {
console.log("Component level array " ,this.props)
return (
<View>
<Text> {this.params.uuid} </Text>
</View>
);
}
}
export default connect(null, { getListThunk })(RenderRequests);
Now console login this shows an empty array:
NOTE the UUID in that log file is a UUID I passed as a prop from the previous screen. As you can see the "getListThunk" is empty.
--------EDIT------------------------ I have added code based on what Vinicius Cleves has said. However I had to make it {list: state} instead of {list: state.listReducer}
I now see it in my console. However, it seems like it shows up then runs the default action and my state gets reset to nothing. Below is a screenshot of my console:
If you see my reducer code I am logging when the default action is being called. Why is it being called so many times after the initial 'GET_LIST' action gets called. This keeps replacing my state with the default state.

getListThunk is a function, as expected. To access the information as you want in this.props, you should provide a mapStateToProps function to connect.
export default connect(
state=>({someVariableName: state.listReducer}),
{ getListThunk }
)(RenderRequests);
Now, the information you were missing on this.props will show under this.props.someVariableName

Related

Context: Cannot read properties of undefined

I'm trying to keep a live state of either the user is signed-in or not, so I can either show him or not, specific elements in the components. Using this code it's supposed to console.log(0) and then console.log(1), but it actually throws an error Cannot read properties of undefined.
./addons/Signed.js:
import { useState, createContext } from "react";
export const SignedContext = createContext();
export default function SignedProvider(props) {
const [SignedIn, setSignedIn] = useState(0);
return (
<SignedContext.Provider value={{ SignedIn, setSignedIn }}>
{props.children}
</SignedContext.Provider>
);
}
./screens/Profile.js:
import { useContext } from "react";
import SignedContext from "../addons/Signed";
...
const ProfileScreen = () => {
const { SignedIn, setSignedIn } = useContext(SignedContext);
console.log(SignedIn);
setSignedIn(1);
console.log(SignedIn);
...
}
...
You are using a named export for SignedContext but using a default import in Profile. Thus, you must use curly braces for your import. The following should change your issue.
import { SignedContext } from ".../addons/Signed"
Edit: If ProfileScreen is not a child of SignedContext.Provider, then this will not work. The general workflow is documented here. Hence, if ProfileScreen is not a child of the Provider, the context won't be available to it.
The ususal way to do this, is to define the context provider as a top level element in your app, if you want the context to be available at a global level in your application.
function App = () => {
const [signedIn, setSignedIn] = useState(0)
const contextValue = React.useMemo(() => ({signedIn, setSignedIn}), [singedIn])
// your application structure must be wrapped inside here.
// as an example I have only used ProfileScreen.
// Usually this is your root stack.
return (
<SignedContext.Provider value ={contextValue}>
<ProfileScreen />
</SignedContext.Provider>
)
}

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.

Error: Actions must be plain objects. Use custom middleware for async actions. (React Native)

I am trying to make it so that if an item called code is not set in state with redux, it is called from AsyncStorage and state is set.
import {AsyncStorage} from 'react-native'
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux'
import {handlePhoneNumber, saveCode} from './../../actions/RegistrationActions';
class EnterPhoneNumberScreen extends React.Component {
componentDidMount() {
let code = this.props.registration.code;
console.log("code is", code);
if(code){
// Do nothing
}else{
console.log("in the else");
this.props.getAndSetCode();
}
}
}
const getAndSetCode = () => dispatch => {
console.log("in get and set Code");
AsyncStorage.getItem('code')
.then((data) => {
console.log("data is ", data);
dispatch(saveCode(data));
console.log("in getAndSetCode method, Code is ", data);
})
}
const mapDispatchToProps = dispatch => (
bindActionCreators({
handlePhoneNumber,
getAndSetCode: () => dispatch(getAndSetCode()),
}, dispatch)
);
export default connect(mapStateToProps, mapDispatchToProps)(EnterPhoneNumberScreen);
The console outputs the following:
LOG code is null
LOG in the else
LOG in get and set Code
LOG data is 3tgvgq
LOG in getAndSetCode method, Code is 3tgvgq
I know thunk is properly installed because it is running elsewhere in the application. saveCode is just a normal action:
export const saveCode = code => ({
type: "SAVE_CODE",
payload: code
})
And this error appears in the iphone11 simulator:
How do I fix this?

Changing state in React native App.js from another component

I'm making authentication in an app, and I'm kind of stuck. I have 2 different navigations. One shows if the user is logged in and another one if not. Basically, a Sign in screen. It's working fine if I change the value manually upon the start. But I can't find a way to change a state when a user signs in, for example. Even though the value in auth module changes, it doesn't update in App.js So how can I update the App.js's state from Sign in screen, for example?
import React, { Component } from 'react';
import { AppRegistry, Platform, StyleSheet, Text, View } from 'react-native';
import DrawerNavigator from './components/DrawerNavigator'
import SignedOutNavigator from './components/SignedOutNavigator'
import auth from './auth'
type Props = {};
export default class App extends Component<Props> {
constructor(props) {
super(props)
this.state = {
isLoggedIn: auth.isLoggedIn
}
}
render() {
return (
(this.state.isLoggedIn) ? <DrawerNavigator /> : <SignedOutNavigator />
);
}
}
AppRegistry.registerComponent('App', () => App)
and my auth module, which is very simple
import { AsyncStorage } from 'react-native';
// try to read from a local file
let api_key
let isLoggedIn = false
function save_user_settings(settings) {
AsyncStorage.mergeItem('user', JSON.stringify(settings), () => {
AsyncStorage.getItem('user', (err, result) => {
isLoggedIn = result.isLoggedIn
api_key = result.api_key
});
isLoggedIn = true
});
}
module.exports.save_user_settings = save_user_settings
module.exports.api_key = api_key
module.exports.isLoggedIn = isLoggedIn
First off, there are loads of ways to approach this problem. Because of this I'm going to try explain to you why what you have now isn't working.
The reason this is happening is because when you assign auth.isLoggedIn to your isLoggedIn state, you are assigning the value once, kind of as a copy. It's not a reference that is stored.
In addition to this, remember, React state is generally only updated with setState(), and that is never being called here, so your state will not update.
The way I would approach this problem without bringing in elements like Redux, which is overkill for this problem by itself, is to look into building an authentication higher order component which handles all the authentication logic and wraps your entire application. From there you can control if you should render the children, or do a redirect.
Auth Component
componentDidMount() {
this._saveUserSettings(settings);
}
_saveUserSettings(settings) {
AsyncStorage.mergeItem('user', JSON.stringify(settings), () => {
AsyncStorage.getItem('user', (err, result) => {
isLoggedIn = result.isLoggedIn
api_key = result.api_key
});
this.setState({isLoggedIn: true});
});
}
render() {
const { isLoggedIn } = this.state;
return isLoggedIn ? this.props.children : null;
}
App.js
render() {
<AuthComponent>
//the rest of authenticated app goes here
</AuthComponent>
}
Here's a really quick, incomplete example. But it should showcase to you how you may want to lay your authentication out. You'll also want to consider error handling and such, however.

How to set config show/hide refresh button on AppBar

Click to see image
Button refresh on AppBar is not refresh on page Dashboard because I just use Component Card but work on page using component List or Datagrid, so I want to config show/hide refresh button on AppBar or how to fix it work for page not use component List or Datagrid.
Sorry I'm not strong in English.
You'll have to fetch some data from the react-admin state for it to work. Indeed, the refresh button just trigger the refreshView action which update the state.admin.ui.viewVersion key of the the react-admin redux state. This key is a simple counter. Internally, we use this counter to check whether we must update some components data. Here is a simple example of a connected Dashboard which can do things when refreshed:
import React, { Component } from "react";
import { connect } from "react-redux";
class Dashboard extends Component {
componentDidMount() {
this.doOnMountAndWhenRefreshed();
}
componentDidUpdate(prevProps) {
if (prevProps.views !== this.props.views) {
this.doOnMountAndWhenRefreshed();
}
}
doOnMountAndWhenRefreshed = () => {
// This is where you do update your component:
// - Make API requests
// - Fetch data from the react-admin store, etc.
};
render() {
const { views } = this.props;
return <div>Refreshed {views} times.</div>;
}
}
const mapStateToProps = state => ({ views: state.admin.ui.viewVersion });
export default connect(
mapStateToProps,
{}
)(Dashboard);
You can see it working in this codesandbox
Edit for newer version of react-admin
import { useVersion } from 'react-admin';
const Dashboard = () => {
const version = useVersion();
return <div>Refreshed {version} times.</div>;
}
In react-admin 4.x I managed to get the desired behaviour like this:
import React from 'react'
import { useQuery } from 'react-query'
const noop = async () => new Date().valueOf()
export const MyDashboard = () => {
const { data } = useQuery('myDashboard', noop)
return (
<div>Last refreshed at {data}</div>
)
}
export default MyDashboard
Note how data represents the value returned by noop().
That way, whenever the user presses the refresh icon in the AppBar, the component is re-rendered.