I am using redux in react-native app. I fetched all values of the category from API & want to pass categoryId to the action.js file.
But I have an issue with passing fetched data to the action.js page. Anyone can give me a solid example of redux for getting data from API and pass it to action#flatlist and #redux
You need to use redux-saga or redux-thunk to handle this scenario.
You can find examples here
Redu-saga
Redux-thunk
You don't need redux-thunk to handler promises you can use async/await and after your promise is solved you can dispatch new data;
import React from 'react';
import { connect } from 'react-redux';
class MyComponent extends React.Component {
constructor() {
this.fetchItems = this.fetchItems.bind(this);
}
async fetchItems() {
const response = await api('api/items');
this.props.dispatch('items', response.data);
}
}
const mapStateToProps = (state) => state;
const mapDispatchToProps = (dispatch) => dispatch;
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
Related
I am trying to retrieve the API_URL from AsyncStorage and make it accessible in all app, the storing and retrieving (in settings screen) is working fine but when I try to load the data in the App.js using useEffect hook, it returns null. Reloading the app is not working but as soon as I save the App.js (using CTRL-S) it works fine.
Please let me know the correct way to do this.
import React, { useEffect, useState } from "react";
import AsyncStorage from "#react-native-async-storage/async-storage";
export default function App() {
const [hostState, setHostState] = useState(null);
const getAHostInfoAsync = async () => {
const hostInfo = AsyncStorage.getItem('host').then(
setHostState(hostInfo)
).then(
console.log(hostState)
);
};
useEffect(() => {
getAHostInfoAsync();
}, []);
module.exports = {
host: hostState
};
}
and using in another file:
import App from "../../../App";
const API_URL = App.host;
I think your issue is in the way you use async/then. instead of async await.
I am not 100% sure that this is your issue. But if I change my async/await function to use async/then the way you are having it, my IDE says that the variable (hostInfo) might not have been initialised. In any case, I think this is a better syntax than with then.
const getAHostInfoAsync = async () => {
const hostInfo = await AsyncStorage.getItem('host')
setHostState(hostInfo)
console.log(hostState)
};
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.
I have been handed a project and been told to use React Hooks instead of Redux as much as possible. Is it possible to replace the reducers and thunks below with React Hooks? Is it worth it to replace?
Reducers/index.js
import { combineReducers } from 'redux'
import {createActions, createReducer, Types as ReduxSauceTypes} from 'reduxsauce'
import { reducer as ProfileReducer } from '#Reducers/Profile'
const appReducer = combineReducers({
profile: ProfileReducer,
// other reducers
})
const { Types, Creators: Actions } = createActions({
resetApp: []
})
const rootReducer = createReducer([], {
[Types.RESET_APP]: (state, action) => {
return appReducer(undefined, action)
//Passing undefined as state will make all the reducers using their initial states.
},
[ReduxSauceTypes.DEFAULT]: (state, action) => {
return appReducer(state, action)
}
})
const resetReduxStore = () => {
return dispatch => {
dispatch(Actions.resetApp())
}
}
export { rootReducer, resetReduxStore }
Reducers/Profile/index.js
import { createActions, createReducer } from 'reduxsauce';
export { default as thunks } from './thunks.js';
/* ------------- Initial State ------------- */
export const INITIAL_STATE = {
user: {},
};
/* ------------- Types and Action Creators ------------- */
export const { Types, Creators } = createActions({
setUser : ['user'],
});
/* ------------- Hookup Reducers To Types ------------- */
export const reducer = createReducer(INITIAL_STATE, {
[Types.SET_USER]: (state, { user }) => {
return {
...state,
user
};
},
});
export default Creators;
"Is it worth it?" is a matter of opinion, but I'll give mine.
Re: useReducer
React now has a useReducer hook that allows you to update a state by dispatching actions. You can use the same reducers that you have already. By default, useReducer only manages the state for the component that it's in. It doesn't come with a context provider. You could create your own context but at that point you are re-creating Redux.
My opinion: Use the React useReducer hook if you have state that it localized to one part of your app. Keep the reducer in some component and pass down callbacks and values via props. If you have state that is global in nature then use Redux.
Re: useState
You asked if you can replace your reducers. As I said, the useReducer hook would use the same sort of reducer so you wouldn't be replacing it. Perhaps the question you need to be asking is do I need a reducer-like system to update this state?
My Opinion: The code that you've posted here is extremely simple and it would be a good candidate for a basic useState hook.
const [user, setUser] = useState();
You could combine this with a context provider and a useContext hook to have a global profile state. If you start having multiple contexts for multiple states -- that's when you want to use Redux instead.
const UserContext = React.createContext([
undefined, // user
() => {} // setUser
]);
// takes no `value` because the state is internal
export const UserProvider = ({children}) => {
const [user, setUser] = React.useState<MaybeUser>();
return (
<UserContext.Provider value={[user, setUser]}>
{children}
</UserContext.Provider>
)
}
export const useUser = () => useContext(UserContext);
Usage in some component:
const Test = () => {
const [user, setUser] = useUser();
...
}
Re: Thunks
Thunks are function of dispatch so they can easily be rewritten with the useDispatch hook, if using Redux. You can use the useSelector hook instead of the getState() argument of a thunk.
There are lots of ways to replace thunks using hooks. But the only thunk that you have here is your resetReduxStore which really doesn't need to be a thunk.
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".
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