Context: Cannot read properties of undefined - react-native

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>
)
}

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.

Redux-Thunk unable to get props in component

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

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.

How to use a custom reducer's state as a permanent filter in a <List>?

I have a custom reducer and a connected component to change its state. Now I'd like to use this state as a permanent filter on List elements.
I understand the List elements are connected to the redux-state, so I hope I'm able to access it through the List component's props, but couldn't find a way how to do that.
The List component is connected but not yours.
import { connect } from "react-redux";
const MyList = ({ is_published, ...props }) => (
<List {...props} filter={{ is_published }}>
</List>
);
const mapStateToProps = state => ({
is_published: state.myCustomReducer.is_published,
});
export default connect(mapStateToProps, undefined)(MyList);
Edit:
Just found out we don't update data when this prop change. This is a bug and you can open an issue about it.
In the mean time, here's a workaround:
Create a custom saga listening to whatever action you use alongside your custom reducer (I'll call it SET_IS_PUBLISHED for my example). This custom saga should put the changeListParams action creator from react-admin with your filter.
It will probably looks like this (not tested):
import { takeEvery, put, select } from 'redux-saga/effects'
import { changeListParams } from 'react-admin'
import { SET_IS_PUBLISHED } from './isPublished'
const getCurrentListParams = (state, resource) => {
const resourceState = state.admin.resources[resource]
return resourceState.list.params
}
function handleSetPublished({ payload }) {
const currentParams = yield select(getCurrentListParams)
const newParams = {
// Keep the current params
...currentParams,
// Override the filter
filter: {
// Keep the current filter
...currentParams.filter,
// Only override the is_published
is_published: payload
}
}
// Dispatch the action for the `posts` resource
yield put(changeListParams('posts', newParams))
}
export default function* () {
yield takeEvery(SET_IS_PUBLISHED, handleSetPublished)
}
just to bring this into 2021, you can use the useSelector redux hook to get hold of your custom state:
import { useSelector } from 'react-redux';
const MyCustomThing = (props) => {
const is_published = useSelector(state => state.customState.is_published);
}
For completeness, react-admin provides a customReducers prop to its <Admin> component so you can extend the redux state with your custom values:
const customStateReducer = (customState = { is_published: false }, { type, payload }) => {
if (type === 'IS_PUBLISHED') customState.is_published = payload.is_published;
return customState;
}
<Admin customReducers={{ customState: customStateReducer }} ...>
etc

How to persist the store values in react native?

In my application,the home page fetches the Json response from my rest API.Then I add the products into the cart array.Initially,my store values are..
const DEFAULT_STATE_TRENDING = {
data:{},
specialoffdata:[],
banner:[],
offerstitle:[],
cart:[],
cartcount:0,
isFetching:false,
dataFetched:false,
error:false,
}
After i added the products the cart array and cart count becomes..
cart:[{...},{...}],
cartcount:cart.length
Now, i close my app.After reloading the app,the cart array becomes empty.How to persist the store values here?
The best way to persist the store values is by using this awesome library. Redux Persist
It contains various methods and levels of persistance, with the ease of use.
Installation and basic usage is well documented on their github docs.
The simplest approach is to use AsyncStorage
let response = await AsyncStorage.getItem('count'); //Read
AsyncStorage.setItem('count', this.state.count + '');
This way, you can access the 'count' in every component.
The most modern way is to use react's new context api, you define the context provider and consume it everywhere:
const ThemeContext = React.createContext('light')
class ThemeProvider extends React.Component {
state = {theme: 'light'}
render() {
return (
<ThemeContext.Provider value={this.state.theme}> //<--- 'light' is exposed here
{this.props.children}
</ThemeContext.Provider>
)
}
}
class App extends React.Component {
render() {
return (
<ThemeProvider>
<ThemeContext.Consumer>
{item => <div>{item}</div>} //<-- you consume the value ('light') here
</ThemeContext.Consumer>
</ThemeProvider>
)
}
}
Either way, it is much lighter and easier than Redux or MobX.
I used the AsyncStorage.In this way i persisted my store state values even after reloading,closing and opening app.
configureStore.js:
import {createStore, applyMiddleware,compose} from 'redux';
import thunk from 'redux-thunk';
import { autoRehydrate } from 'redux-persist';
import allReducers from './reducers';
import { AsyncStorage } from 'react-native';
const middleware = [ thunk,
];
export default function configureStore() {
let store= createStore(
allReducers,
{},
compose(applyMiddleware(...middleware),autoRehydrate())
);
console.log("Created store is"+store);debugger
return store;
}
App.js:
const store = configureStore();
export default class App extends Component {
componentDidMount() {
console.log("App.js started and its store value is"+store);debugger
SplashScreen.hide()
persistStore(
store,
{
storage : AsyncStorage,
whitelist : ['trending']
}
);
}
render() {
return (
<Provider store={store}>
<MyStack/>
</Provider>
);
}
}