How to read redux store from non React components? - react-native

I am building my first react native app with redux.
My store configuration is as follows
function configureStore(onComplete: ?() => void) {
const store = autoRehydrate()(createMyAppStore)(reducers);
persistStore(store, {storage: AsyncStorage}, onComplete);
if (isDebuggingInChrome) {
window.store = store;
}
return store;
}
I have an authentication action as follows
export function authentiate(credentials) {
return (dispatch) => {
return LoginApi.authenticate(credentials)
.then(response => {
return response.json().then(function(json) {
dispatch(onAuthSuccess(json));
});
})
.then(response => { dispatch(getUserInfo()); })
.catch(error => {
throw(error);
});
};
}
My getUserInfo action is as follows
export function getUserInfo() {
return (dispatch) => {
return LoginApi.gerUserInfo()
.then(result => { dispatch(onGetUserInfoSuccess(result.json())); })
.catch(error => {
throw(error);
});
};
}
My authentication reducer is as follows
function auth(state: State = initialState, action: Action): State {
switch (action.type) {
case types.AUTH_SUCCESSFUL :
return [...state, Object.assign({}, action.auth)];
default :
return state;
}
return state;
}
The LoginApi is a simple ES6 class. The getUserInfo requires details like access token and other parameters obtained from the LoginApi.authenticate call.
How do I read the authentication information from the redux store from a non-react component?

The second argument to redux-thunk is getState
export function getUserInfo() {
return (dispatch, getState) => {
const state = getState() // or cherry pick what you need
return LoginApi.gerUserInfo(state)
.then(result => { dispatch(onGetUserInfoSuccess(result.json())); })
.catch(error => {
throw(error);
});
};
}
https://github.com/gaearon/redux-thunk#composition
Which is just the stores own getState function.
http://redux.js.org/docs/api/Store.html#getState
you can export / import your store and call getState directly if you want.
let store = createStore(...)
export { store }
// anywhere.js
import { store } from './your-store.js'
let state = store.getState()

Related

Unable to update state in redux when using #reduxjs/toolkit

I currently started learning redux. My code was working perfectly with core redux, then I tried out #reduxjs/toolkit and now I'm unable to access the function to change the state in the store. Here is my code of reducer.
const seasonEdits = createSlice({
name: "seasons",
initialState: [],
reducers: {
addSeason(state, action) {
state.push(action.payload);
console.log("this here");
},
removeSeason(state, action) {
state.filter((season) => season.id !== action.payload);
},
markComplete(state, action) {
state.map((season) => {
if (season.id == action.payload) season.isWatched = !season.isWatched;
});
},
},
});
export const { addSeason, removeSeason, markComplete } = seasonEdits.actions;
export default seasonEdits.reducer;
and my store.js file
import { configureStore } from "#reduxjs/toolkit";
import seasonReducer from "./reducer";
export default store = configureStore({
reducer: {
seasons: seasonReducer,
},
});
and the add.js file which has add functionality. Calling a handleSubmit function which is creating an object and adding it to an array which is the state in store.
const handleSubmit = async () => {
try {
if (!name || !totalNoSeason) {
return alert("Please add both fields");
}
const seasonToAdd = {
id: shortid.generate(),
name,
totalNoSeason,
isWatched: false,
};
addSeason(seasonToAdd);
navigation.navigate("Home");
} catch (error) {
console.log(error);
}
};
const mapDispatchToProps = (dispatch) => {
return {
addSeason: (data) => dispatch(addSeason(data)),
};
};
Add.propTypes = {
addSeason: propTypes.func.isRequired,
};
export default connect(null, mapDispatchToProps)(Add);
The issue is that array.map() and array.filter() return new arrays! Right now your reducers are calling those functions, and then just throwing away the new arrays:
removeSeason(state, action) {
// The return value is thrown away and ignored!
state.filter((season) => season.id !== action.payload);
},
You need to return the new value:
removeSeason(state, action) {
// Now RTK will see the new return value
return state.filter((season) => season.id !== action.payload);
},
See https://redux-toolkit.js.org/usage/immer-reducers#resetting-and-replacing-state for more details.

Unit tests, check if function have been called

I need to implement a test that checks if the function has been called on the button click
onSave (e) {
this.$qiwaApi.createDimension()
.then(() => {})
.catch(err => this.showSnackbar(err.message))}
I need to test the function createDimension. In my test i mocked it
const createComponent = () => {
wrapper = mount(dimensions, {
store,
localVue,
mocks: {
$qiwaApi: {
createDimension: function (e) {
return new Promise((resolve) => { resolve({}) })
}
}
},
router
})
}
In the project, the function exported this way
export default $axios => ({
createDimension (data, surveyId) {
return $axios.post(`/lmi-admin/surveys/${surveyId}/dimension`, {
data: {
attributes: {
...data
}
}
})
}
})
I expect this test to work. But for some reason wrapper.qiwaApi or wrapper.createDimension return undefined
expect(wrapper.$qiwaApi.createDimension).toHaveBeenCalled()
The wrapper doesn't provide access to your mocks that way.
You would have to hold a reference to a jest.fn(), and then verify the calls on that reference directly instead of trying to pull it out of the wrapper:
it('calls createDimension on button click', async () => {
const createDimension = jest.fn(() => Promise.resolve())
const wrapper = mount(dimensions, {
mocks: {
$qiwaApi: {
createDimension
}
}
})
await wrapper.find('[data-testid="save"]').trigger('click')
expect(createDimension).toHaveBeenCalled()
})
demo

Why data is not loading from this dispatch action?

I am trying to learn redux.
I watch some tutorials and follow along with them. These tutorials are with class component.
So I try to change these into functional component.
Since I am just learning and not trying to make a big project I put actions, reducers and types into 1 file.
This is that file
import axios from 'axios';
export const FETCH_NEWS = 'FETCH_NEWS';
// Reducer
const initialState = {
newsList: [],
};
export const articlesReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_NEWS:
return {...state, newsList: action.payload};
default:
return state;
}
};
export const fetchNews = () => (dispatch) => {
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((res) => {
dispatch({
type: FETCH_NEWS,
payload: res.data,
});
})
.catch((err) => {
console.log(err);
});
};
So I am using fetchNews props in News component
News component is like this
import { fetchNews }from '../../ducks/modules/Articles'
useEffect(() => {
fetchNews();
console.log('##############################')
console.log(newsList)
console.log('##############################')
},[])
const News = ({navigation, newsList, fetchNews}) => {
return (<View> .... </View>)
}
News.propTypes = {
fetchNews: PropTypes.func.isRequired,
newsList: PropTypes.array.isRequired
}
const mapStateToProps = state => {
return {
newsList: state.articlesReducer.newsList
}
}
export default connect(mapStateToProps, { fetchNews })(News);
As you can see I am console.logging in the useEffect hooks , I am console logging because no data are being loaded in the device
Here is a picture of empty array when component is mounted
My store component is like this
const reducer = combineReducers({
articlesReducer
});
const store = createStore(reducer, applyMiddleware(thunk,logger));
You are not dispatching the action correctly. I have added simpler way to use redux with function based components. You don't need to use connect.
export const fetchNews = () => (dispatch) => {
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((res) => {
dispatch({
type: FETCH_NEWS,
payload: res.data,
});
})
.catch((err) => {
console.log(err);
});
};
export const selectNewsList = (state) => state.newsList; // this is known as a selector.
And your view will be:
import { useSelector, useDispatch } from 'react-redux';
import { fetchNews, selectNewsList }from '../../ducks/modules/Articles'
const News = () => {
const newsList = useSelector(selectNewsList);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchNews());
},[])
console.log(newsList); // This will print empty array first, but will print again as data is populated.
return (<View> .... </View>)
}

How to add a module getters in composition api?

I am using vue 2, installed composition api. How can I add Getters?
Usually:
computed: {
...mapGetters("Auth", ["isLogged"])}
..........................................................................
setup() {
const title_app = ref("Name App");
const logout = () => {
store
.dispatch("Auth/logout")
.then(() => {
router.push({ name: "About" });
})
.catch((err) => {
console.log(err);
});
};
return {
title_app,
logout,
};
},
You can do something like this:
import { computed } from 'vue'
export default {
setup (props, { root }) {
const isLogged = computed(() => root.$store.Auth.getters.isLogged)
return {
isLogged
}
}
}

Integrating Pull-To-Refresh in a ScrollView with Redux in React Native

I am trying to add pull-to-refresh functionality to a <ScrollView> using a refreshControl and integrate it with Redux.
Example from https://facebook.github.io/react-native/docs/refreshcontrol:
_onRefresh = () => {
this.setState({refreshing: true});
fetchData().then(() => {
this.setState({refreshing: false});
});
}
My problem is that my own fetchData function dispatches an action for the reducer to handle, so as far as I understand it, it is not a thenable promise. So I don't fully understand the integration with Redux in this case. What do I need to change in my code to be able to set refreshing to false as in the above example?
PostFeedScreen.js
// on mount, fetch all posts from the API
componentDidMount() {
this.props.fetchPostsFromAPI();
}
_onRefresh = () => {
this.setState( { refreshing: true } );
this.props.fetchPostsFromAPI().then( () => { // error
this.setState( { refreshing: false } );
});
}
// map dispatch to props
const mapDispatchToProps = ( dispatch ) => {
return {
fetchPostsFromAPI: () => {
dispatch( fetchPostsFromAPI() );
}
}
}
PostActions.js
// fetch all posts
export function fetchPostsFromAPI() {
return( dispatch ) => {
let loadData = new Promise( ( resolve, reject ) => {
resolve( postsInitial ); // using dummy data for now
})
loadData
.then( posts => dispatch( fetchPostsSuccess( posts ) ) );
}
// is used if posts were succesfully loaded
function fetchPostsSuccess( posts ) {
return {
type: PostConstants.FETCH_POSTS_SUCCESS,
data: posts,
}
}
PostReducer.js
const PostReducer = ( state = initialState, action ) => {
switch( action.type ) {
// if fetching data was successful
case PostConstants.FETCH_POSTS_SUCCESS: {
return {
...state,
posts: action.data,
}
}
default: {
return state
}
}
You get an error cause you call .then on something who don't return a promises. Just add return in front of your loadData, cause you can chain promises.
export function fetchPostsFromAPI() {
return dispatch => {
let loadData = new Promise((resolve, reject) => {
resolve(postsInitial);
});
return loadData.then(posts => dispatch(fetchPostsSuccess(posts)))
};
}