First of all, I am new with react. It has been two weeks since I am working with it
I am trying to use "realm-web" with react (web). I would like to write realm provider in order to access realmApp everywhere in my application. However my attempts didn't go well. The application is giving " Uncaught (in promise) RangeError: Maximum call stack size exceeded". But beside this error, I am sure there is more to be handled.
Here is my implementation:
const RealmProvider: React.ForwardRefRenderFunction<HTMLElement, PropTypes> = ({ app: realmApp, store, children }) => {
let realm = useTypedSelector(store => store.realm);
let app = useRef<Realm.App>();
const credentials = React.useMemo(() => Realm.Credentials.apiKey(process.env.REACT_APP_REALM_APP_API_KEY!), []);
useEffect(() => {
app.current = realmApp
console.log(realmApp)
if (app.current.currentUser == null) {
loadingRealmApp()
app.current.logIn(credentials)
.then((res) => store.dispatch(storeRealmApp(res)))
//.catch(reason => console.log("RealmError", reason));
}
//Specify how to clean up after this effect:
return function cleanup() {
app.current?.currentUser?.logOut().then(() =>
store.dispatch(unloadRealmApp())
);
};
}, [realmApp, store]);
return (
<>
{ React.Children.only(children)}
</>
);
}
export default RealmProvider;
Reducer
// Actions
const STORE_REALM_APP = 'atlas/realm/STORE_REALM_APP'
const STORE_REALM_APP_ERROR = 'atlas/realm/STORE_USER_ERROR'
const UNLOAD_REALM_APP = 'atlas/realm/UNLOAD_REALM_APP'
const LOADING_REALM_APP = 'atlas/realm/LOADING_REALM_APP'
type State = {
//realmApp?: Realm.App
user?: Realm.User
isLoadingRealmApp: boolean
}
const initialState: State = {
//realmApp: undefined,
user: undefined,
isLoadingRealmApp: false
};
type RealmAction = {
payload: Realm.User
} & Action;
// Reducer
const realmReducer = function (state: State = initialState, action: RealmAction): State {
switch (action.type) {
case STORE_REALM_APP:
return {
...state,
isLoadingRealmApp: false,
user: action.payload
}
case LOADING_REALM_APP:
return {
...state,
isLoadingRealmApp: true
}
case STORE_REALM_APP_ERROR:
case UNLOAD_REALM_APP:
return {
...state,
user: undefined,
isLoadingRealmApp: false
}
default:
return state
}
}
export default realmReducer;
export function storeRealmApp(app: Realm.User) {
console.log("storeRealmApp", app)
return {
type: STORE_REALM_APP,
payload: app
}
}
export function loadingRealmApp() {
return {
type: LOADING_REALM_APP
}
}
export function storeRealmAppError(reason: any) {
return {
type: STORE_REALM_APP_ERROR,
payload: reason
}
}
export function unloadRealmApp() {
return {
type: UNLOAD_REALM_APP
}
}
Realm.ts
const realmApp: Realm.App = new Realm.App({ id: process.env.REACT_APP_REALM_APP_ID!, app: { name: "Atlas" } });
//const mongodb = realmApp.currentUser!.mongoClient("mongodb-atlas")
const useMongodb = () => {
const user = useTypedSelector(store => store.realm.user);
console.log(user)
return user!.mongoClient("mongodb-atlas");
}
export { realmApp, useMongodb }
I guess I am doing something wrong, or this is not the way of doing this. I need some help :)
In case someone needs it, I figure it out.
import React from "react";
import * as Realm from "realm-web";
type ContextType = {
currentUser: Realm.User,
logIn: (credentials: Realm.Credentials) => void
logOut: () => void
} & Realm.App
const RealmAppContext = React.createContext<ContextType>({
} as any);
export const useRealmApp = () => {
const app = React.useContext(RealmAppContext);
if (!app) {
throw new Error(
`You must call useRealmApp() inside of a <RealmAppProvider />`
);
}
return app;
};
export const useMongodb = () => {
const app = React.useContext<ContextType>(RealmAppContext);
const mongodb = app.currentUser.mongoClient("mongodb-atlas")
if (!mongodb) {
throw new Error(
`You must call useRealmApp() inside of a <RealmAppProvider />`
);
}
return mongodb;
};
type PropTypes = {
appId: string,
//children: JSX.Element
}
export const RealmAppProvider: React.ForwardRefRenderFunction<HTMLElement, PropTypes> = ({ appId, children }) => {
const [app, setApp] = React.useState(new Realm.App(appId));
React.useEffect(() => {
setApp(new Realm.App(appId));
}, [appId]);
// Wrap the Realm.App object's user state with React state
const [currentUser, setCurrentUser] = React.useState(app.currentUser);
async function logIn(credentials: Realm.Credentials) {
await app.logIn(credentials);
// If successful, app.currentUser is the user that just logged in
setCurrentUser(app.currentUser);
}
async function logOut() {
// Log out the currently active user
await app.currentUser?.logOut();
// If another user was logged in too, they're now the current user.
// Otherwise, app.currentUser is null.
setCurrentUser(app.currentUser);
}
const wrapped = { ...app, currentUser, logIn, logOut };
return (
<RealmAppContext.Provider value={wrapped as any}>
{children}
</RealmAppContext.Provider>
);
};
Related
i am trying to send the error messages that sent from my server ( express ) to axios and the error message displays in toastify component but the error message doesn't show up here is the login axios function with the toastify how can i display toastify message inside my page from redux ?
here is my code :
// redux controller
const login = async (username, password) => {
await axios.post("/login",{username,password,},
{ withCredentials: true });};
// reducer page
export function generateError(prop) {
return function (dispatch) {
dispatch({
type: "USER_FAIL"
});
toast.error(prop);
};
}
export function generateSuccess(prop) {
return function (dispatch) {
dispatch({
type: "USER_SUCCESS"
});
toast.success(prop);
};
}
export const login = createAsyncThunk(
"/login",
async ({ username, password }) => {
try {
const data = await authService.login(username, password);
if (data) {
if (data.errors) {
const { username, password } = data.errors;
if (username) generateError(username)
else if (password) generateError(password);
} else {
generateSuccess(data.success);
}
}
return { user: data };
} catch (error) {
console.log(error);
}
}
);
// login page
const handleSubmit = (e) => {
e.preventDefault();
dispatch(login({ username, password }));
}
i am using react-tostify and #redux-toolkit but the message doesn't display inside my page
i fixed it and here is my code :
// auth.js ( redux page )
export const login = createAsyncThunk(
"/login",
async ({ username, password }) => {
try {
const {data} = await axios.post(
"/login",
{
username,
password,
},
{ withCredentials: true }
);
return { user: data };
} catch (error) {
console.log(error);
}
});
const initialState = user
? { isLoggedIn: true, user }
: { isLoggedIn: false, user: null };
const authSlice = createSlice({
name: "auth",
initialState,
extraReducers: {
[login.fulfilled]: (state, action) => {
state.isLoggedIn = true;
state.user = action.payload.user;
},
[login.rejected]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
[logout.fulfilled]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
}})
const { reducer } = authSlice; export default reducer;
Login Page :
const { isLoggedIn } = useSelector((state) => state.auth);
const dispatch = useDispatch();
const handleSubmit = (e) => {
e.preventDefault();
dispatch(login({ username, password })).then(data => {
console.log(data)
if (data.payload.user) {
if (data.payload.user.errors) {
const { username, password } = data.payload.user.errors;
if (username) generateError(username)
else if (password) generateError(password);
} else {
generateSuccess(data.success);
navigate("/dashboard");
}
}
})
}
i realized when i back the data it has an object name payload i used it to get the error messages from express and then i put the message in toastify function gettingError and here it is
const generateError = error => {
toast.error(error, {
position: "bottom-right",
})
}
Hai I'm also looking for the same problem while searching I found a solution at with this : react-toastify-with-redux
my Code : authAction.js
import 'react-toastify/dist/ReactToastify.min.css';
import { toast} from 'react-toastify';
export const registerUser = (userData) => dispatch =>{
axios.post('user/register',userData)
.then(res=>toast.success('Your Account Created Successfully 👍'))
.then(res=> window.location = '/authentication/sign-in')
.catch(err=>dispatch(
{
type: GET_ERRORS,
payload: err.response.data
}
),toast.error("Error 😣"))
// .catch((err)=> {return })
};
On your signUp page just add
<ToastContainer />
That's all ...
This answer is probably late. But I came across this problem and decided to do it my way. I know there is toast. promise to handle promises and I don't want to call dispatch.then every time. So I can up with passing dispatch to my action wrapper. Here is my code.
// utils.ts
type ArgumentTypes<F extends CallableFunction> = F extends (
...args: infer A
) => any
? A[0]
: never;
export const withToast = <T = AnyAction | typeof createAsyncThunk>(
action: T,
{ pending, error, success }: ToastPromiseParams<T>
) => {
return (
dispatch: ReturnType<typeof useAppDispatch>,
actionParams?: ArgumentTypes<T & CallableFunction> | void
) => {
const promise = dispatch(
(action as CallableFunction)(actionParams as any)
).unwrap();
toast.promise(promise, {
pending,
error,
success,
});
};
};
// actions.ts
export const login = createAsyncThunk(
"user/login",
async (payload: {
email: string;
password: string;
}): Promise<Partial<LoginAPIResponse>> => {
const { data } = await axios.post(`${API}/${LOGIN_EP}/`, payload);
return data;
}
);
export const loginWithToast = withToast(login, {
pending: "Logging in...",
error: {
render: (error: any) => {
return error?.password || error?.email
? "Invalid email or password"
: "Something went wrong";
},
},
success: "Logged in successfully",
});
// usage in component
const dispatch = useAppDispatch();
loginWithToast(dispatch, {
email: values.email.value,
password: values.password.value,
});
First createAsyncThunk:
import { coreAxios } from "utilities/axios"; // Own customized axios
import { createAsyncThunk } from "#reduxjs/toolkit";
const BASE_URL = process.env.REACT_APP_MAIN_URL
export const GetProducts = createAsyncThunk(
"inventory/GetProducts",
async () => {
const {data} = await coreAxios.get(`${BASE_URL}/api/product/list/`);
return data
}
);
Second createSlice:
import { createSlice } from "#reduxjs/toolkit";
import { GetProducts } from "services/inventory/product.service";
import { toast } from 'react-toastify';
export const productSlice = createSlice({
name: "products",
initialState: {
productsList: [],
productsLoading: false,
productsError: null,
},
extraReducers:
(builder) => {
builder.addCase(GetProducts.pending, (state) => {
toast.loading('Promise is pending...')
state.productsLoading = true
});
builder.addCase(GetProducts.fulfilled, (state, action) => {
toast.dismiss();
toast.success('Promise resolved 👌');
state.productsList = action.payload
state.productsLoading = false
state.productsError = null
});
builder.addCase(GetProducts.rejected, (state, action) => {
toast.dismiss();
toast.error('Promise rejected 🤯 😣')
state.productsLoading = false
state.productsError = action.error?.message
});
},
});
export default productSlice.reducer;
Third page:
import { ToastContainer } from 'react-toastify';
import { useSelector, useDispatch } from "react-redux";
import { GetProducts } from 'services/inventory/product.service';
const Product = () => {
const { productsList, productsLoading, productsError } = useSelector((state) => state.products);
const dispatch = useDispatch();
useEffect(() => {
dispatch(GetProducts());
}, []);
return (
<div className="grid crud-demo">
<h1>Hello Alim</h1>
<ToastContainer />
</div>
);
}
import create from 'zustand';
import createContext from 'zustand/context';
import { persist } from 'zustand/middleware';
let store;
const initialState = {
loading: false,
cart: {
cartItems: {},
invoiceData: {},
count: 0,
},
};
const zustandContext = createContext();
export const Provider = zustandContext.Provider;
// An example of how to get types
/** #type {import('zustand/index').UseStore<typeof initialState>} */
export const useStore = zustandContext.useStore;
export const initializeStore = (preloadedState = {}) => {
return create(
persist(
(set, get) => ({
...initialState,
...preloadedState,
updateCart: (cartData) => {
set({
cart: cartData,
});
},
setLoading: (val) => {
set({
loading: val,
});
},
modifyCart: (product, qty, type) => {
const cartData = get().cart;
// cart operations
set({
cart: tmpCartData,
});
},
}),
{
name: 'cartData',
}
)
);
};
export function useCreateStore(initialState) {
const [cartData, setCartData] = useState(null);
const [userCart, setCart] = useLocalStorage('cartData', {});
const { state: { cart = {} } = {} } = userCart;
if (typeof window === 'undefined') {
return () => initializeStore(initialState);
}
store = store ?? initializeStore(initialState);
useLayoutEffect(() => {
if (initialState && store) {
store.setState({
...store.getState(),
...initialState,
});
}
}, [initialState]);
useLayoutEffect(() => {
(async () => {
store.setState({
...store.getState(),
cart: { ...cart },
loading: true,
});
})();
}, []);
return () => store;
}
This code is inspired by Zustand documentation and by the NextJS and Zustand boilerplate. I need to sync this data with the browser's localstorage. However, calling the 'set' method inside modifyCart causes an infinite render. I have not found enough documentations regarding this.
How should I go about debugging such an issue?
I'm attempting to import a function defined in a 'context' file so I can access the user data across the whole app easily.
When importing it, and calling it in my 'useEffect' however, it's claiming it isn't a function - when it clearly is.
For reference, here is the component which I'm calling the context function from:
import { Context } from '../context/authContext';
const LoadingScreen = ({ navigation }) => {
const { checkSignedIn } = useContext(Context)
useEffect(() => {
checkSignedIn()
}, [])
return (
<View style={styles.mainView}>
<ActivityIndicator style={styles.indicator} />
</View>
)
}
Here is the context file itself (in full):
import createDataContext from './createDataContext';
import { navigate } from '../navigationRef';
import { Magic } from '#magic-sdk/react-native';
const m = new Magic('API key');
const authReducer = (state, reducer) => {
switch (action.type) {
case 'checkComplete':
return { ...state, userDetails: action.payload };
default:
return state;
}
};
const checkSignedIn = dispatch => {
return async () => {
// Set variable which calls Magic to check if signed in
const checkMagic = await m.user.isLoggedIn();
// If user signed in, redirect to dashboard and save details in state
if (checkMagic === true) {
const { issuer, email } = await m.user.getMetaData();
dispatch({
type: 'checkComplete',
payload: [issuer, email]
});
navigate('authorisedFlow');
// If user not signed in, redirect to welcome page and set state as null
}
else {
navigate('loginFlow');
}
};
};
export const { Context, Provider } = createDataContext( authReducer, { checkSignedIn }, { userDetails: null } );
Here is the 'helper' function called 'createDataContext' which iterates over the dispatch and defines it without having to repeat create context code:
import React, { useReducer } from 'react';
export default (reducer, actions, defaultValue) => {
const Context = React.createContext();
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, defaultValue);
const boundActions = {};
for (let key in actions) {
boundActions[key] = actions[key].dispatch;
}
return (
<Context.Provider value={{ state, ...boundActions }}>
{children}
</Context.Provider>
)
};
return { Context, Provider }
};
I don't think the navigate helper is necessary to show here, but happy to put in if needed. It seems to be more of a problem with the function itself!
If anybody can point out what I'm doing wrong here it'd be much appreciated!
Please let me know if any of that is unclear.
I have an API endpoint that returns a list of users in an 'application/stream+json' type response. The items are separated by a new line character.
Example data can be seen here.
Component
class UserList extends Component {
componentDidMount() {
const { fetchUsers } = this.props;
fetchUsers();
}
render() {
const { isFetching = false, users = [] } = this.props;
if (isFetching) {
return <Loader message="Users are loading..." />;
}
if (!users || users.length === 0) {
return 'No users found.';
}
const children = users
.map(user => <UserListItem key={user.id} user={user} />);
return (
<div className="UserList">
<Paper>
<List>
<Subheader>Users</Subheader>
{children}
</List>
</Paper>
</div>
);
}
}
UserList.propTypes = {
users: PropTypes.arrayOf(PropTypes.any),
isFetching: PropTypes.bool.isRequired,
fetchUsers: PropTypes.func.isRequired,
};
UserList.defaultProps = {
users: [],
};
function mapStateToProps(state) {
const { users, isFetching } = state.users;
return {
users,
isFetching,
};
}
function mapDispatchToProps(dispatch) {
return {
fetchUsers: bindActionCreators(actions.fetchUsers, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(UserList);
Reducer
const initialState = {
users: [],
isFetching: false,
};
function fetchUsers(state) {
return {
...state,
isFetching: true,
};
}
function fetchUsersItemReceived(state, action) {
const { user } = action;
return {
...state,
users: [...state.users, user],
isFetching: false,
};
}
export default function (state = initialState, action) {
switch (action.type) {
case actionTypes.FETCH_USERS_REQUEST:
return fetchUsers(state);
case actionTypes.FETCH_USERS_ITEM_RECEIVED:
return fetchUsersItemReceived(state, action);
default:
return state;
}
}
Action (the parser is the Streaming JSON Parser found here)
export function fetchUsers() {
return {
type: actionTypes.FETCH_USERS_REQUEST,
};
}
function fetchUsersItemReceived(user) {
return {
type: actionTypes.FETCH_USERS_ITEM_RECEIVED,
user,
};
}
function fetchUsersSuccess() {
return {
type: actionTypes.FETCH_USERS_SUCCESS,
};
}
function fetchUsersFailure(error) {
return {
type: actionTypes.FETCH_USERS_FAILURE,
error,
};
}
function getJsonStream(url) {
const emitter = new Subject();
const req$ = RxHR
.get(url)
.flatMap(resp => resp.body)
.subscribe(
(data) => {
parser.write(data);
parser.onValue = (value) => {
if (!parser.key) {
emitter.next(value);
}
};
},
err => emitter.error(err),
() => emitter.complete(),
);
return emitter;
}
export const fetchUsersEpic = action$ =>
action$.ofType(actionTypes.FETCH_USERS_REQUEST)
.concatMap(() => getJsonStream(`${api.API_BASE_URL}/user`))
.map(user => fetchUsersItemReceived(user));
configureStore.js
const logger = createLogger();
const epicMiddleware = createEpicMiddleware(rootEpic);
const createStoreWithMiddleware = applyMiddleware(epicMiddleware, logger)(createStore);
export default function configureStore(initialState) {
return createStoreWithMiddleware(rootReducer, initialState);
}
While the list component should be refreshed after EACH item is received, it is refreshed AFTER the whole list is received. Can someone point me to the blocking point in the code?
This isn't a solution to your particular issue (unless by accident lol) but I think a custom Observable is a better fit in this situation instead of a Subject. You can also hook into the Parser's error callback too.
Figured that others searching for streaming JSON with rxjs later might find this handy (untested)
function streamingJsonParse(data$) {
return new Observable((observer) => {
const parser = new Parser();
parser.onError = (err) => observer.error(err);
parser.onValue = (value) => {
if (!parser.key) {
observer.next(value);
}
};
// return the subscription so it's correctly
// unsubscribed for us
return data$
.subscribe({
next: (data) => parser.write(data),
error: (e) => observer.error(e),
complete: () => observer.complete()
});
});
}
function getJsonStream(url) {
return RxHR
.get(url)
.mergeMap(resp => streamingJsonParse(resp.body));
}
When you've had a chance to put together that jsbin let me know!
Turns out the problem was with the jsonParse lib. Switching to oboe.js
fixed it. Using the "!" node selector to select multiple root JSON elements i was able to transform the character stream to a user object stream.
Action
function getJsonStream(url) {
const emitter = new Subject();
const emitter = new Subject();
oboe(url)
.node('!', (item) => {
emitter.next(item);
})
.fail((error) => {
emitter.error(error);
});
return emitter;
}
export const fetchUsersEpic = action$ =>
action$.ofType(actionTypes.FETCH_USERS_REQUEST)
.switchMap(() => getJsonStream(`${api.API_BASE_URL}/user`))
.map(user => fetchUsersItemReceived(user));
I have to display a table with lot of data. There is a pagination button which display 10 record per page. I have one button called "FETCH" on-click of it table is populating. How do load my table on load of the page.
action.js
import fetch from 'isomorphic-fetch';
export const ADD_BENEFICIARY = 'ADD_BENEFICIARY';
export const REMOVE_BENEFICIARY = 'REMOVE_BENEFICIARY';
export const ADD_PLAN_TO_COMPARE = 'ADD_PLAN_COMPARE';
export const REMOVE_PLAN_FROM_COMPARE = 'REMOVE_PLAN_COMPARE';
export const SHOW_PLAN_COMPARE = 'SHOW_PLAN_COMPARE';
export const NEXT_PAGE_PLAN_LIST = 'NEXT_PAGE_PLAN_LIST';
export const PREV_PAGE_PLAN_LIST = 'PREV_PAGE_PLAN_LIST';
export const REQUEST_PAGE_PLAN_LIST = 'REQUEST_PAGE_PLAN_LIST';
export const RECEIVE_PAGE_PLAN_LIST = 'RECEIVE_PAGE_PLAN_LIST';
export const showNextPageOfPlans = () => {
return {
type: NEXT_PAGE_PLAN_LIST
}
}
export const showPreviousPageOfPlans = () => {
return {
type: PREV_PAGE_PLAN_LIST
}
}
export const requestPageOfPlans = (startIdx, pageSize) => {
return {
type: REQUEST_PAGE_PLAN_LIST,
start: startIdx,
pageSize: pageSize
}
}
export const receivePageOfPlans = (startIdx, json) => {
return {
type: RECEIVE_PAGE_PLAN_LIST,
start: startIdx,
plans: json
}
}
export const fetchPlans = (startIdx, pageSize) => {
var str = sessionStorage.getItem('formValue'); //JSON.stringify(formValues);
return function (dispatch) {
dispatch(requestPageOfPlans(startIdx, pageSize));
return fetch('http://172.16.32.57:9090/alternatePlans/plans/list/', {method: 'post', body: str, headers: new Headers({'Content-Type': 'application/json'}) })
.then(response => response.json())
.then(json =>
dispatch(receivePageOfPlans(startIdx, json))
)
}
}
reducer.js
import { REQUEST_PAGE_PLAN_LIST, RECEIVE_PAGE_PLAN_LIST,
NEXT_PAGE_PLAN_LIST, PREV_PAGE_PLAN_LIST } from './actions';
const initialPaging = {
startIndex: 0,
lastIndex: 0,
pageSize: 10
}
const paging = (state = initialCurrentPage, action) => {
switch (action.type) {
case NEXT_PAGE_PLAN_LIST:
if (state.startIndex+state.pageSize <= state.lastIndex) {
return { ...state, startIndex: state.startIndex+state.pageSize };
}
else {
return state;
}
case PREV_PAGE_PLAN_LIST:
if (state.startIndex-state.pageSize >= 0) {
return { ...state, startIndex: state.startIndex-state.pageSize };
}
else {
return state;
}
case REQUEST_PAGE_PLAN_LIST:
return { ...state, isFetching: true };
case RECEIVE_PAGE_PLAN_LIST:
return { ...state, isFetching: false };
default:
return state;
}
}
var initialPlans = [];
const plans = (state = initialPlans, action) => {
switch (action.type) {
case RECEIVE_PAGE_PLAN_LIST:
return action.plans.plans;
default:
return state;
}
}
const allReducers = (state = {}, action) => {
let items = plans(state.plans, action);
return {
plans: items,
paging: paging({ ...initialPaging, ...state.paging, lastIndex: items.length-1 }, action)
}
}
export default allReducers;
P.S. I am new to react-redux. Official Documentation is good but very less explanation is given.
You call it from the componentDidMount() of the react component for your page, or wherever it makes sense. So in that component file:
import { requestPageOfPlans } from 'actions';
import React from 'react';
import { connect } from 'react-redux';
class MyComponent extends React.Component {
componentDidMount() {
this.props.requestPageOfPlans();
}
}
export default connect((state) => state, { requestPageOfPlans })(MyComponent);
So the key here is the connect setup. The first parameter is how you wish to transform state (your reducers) into data for props. Adjust that as you need to. The second one is what actions you wish to bind. You can also import dispatch manually, but I personally like this pattern. It sets up the actions as props on the component, and you can call it that way. Just pass the arguments to it as you need to. This page here: https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options explains the connect function in more detail.
Edit
Given you're paginating on the front end, you'll need to adjust the connect mapStateToProps function to pass the pagination data down to the component, and then loop through it to display what you need. Personally I would do the pagination on the back end, and just do new requests for each page. Depends on how many records you're expecting to have.
So in the connect do something like this:
export default connect((state) => {
return {
startIndex: state.paging.lastIndex,
pageSize: state.paging.pageSize,
lastIndex: state.paging.lastIndex,
plans: state.plans
};
}, { requestPageOfPlans })(MyComponent);
Then in your component, loop through the plans using those indexes:
render() {
const plans = [];
const maxIndex = Math.min(this.props.startIndex + this.props.pageSize, this.props.lastIndex);
for (let i = this.props.startIndex || 0; i < maxIndex; i++) {
const plan = this.props.plans[i];
plans.push(<Plan key={plan.id} plan={plan} />);
}
return (
<ul>{plans}</ul>
);
}
Making some assumptions again on how you plan to render it, if you have a component called Plan, etc. But that's roughly how you can work with it.
componentDidMount is a good timing to load your fist page.
BTW, In my opinion, the page change is not the action, it is just some param for load your data.
ActionCreator
const userurl = '/rest/users'
export const loadUsers = (page = 0) => {
return (dispatch) => {
axios.get(userurl+"?page="+page)
.then(function(resp) {
dispatch(loadUsersOK(resp.data._embedded.users, resp.data.page));
})
.catch(function(error) {
...
})
};
}
Component JSX
<span style={{marginRight:15, fontSize: 14, color: '#333'}}>{page.number*page.size+1}-{page.number*page.size+page.size} of {' '} {page.totalElements}</span>
<FlatButton style={flatButtonStyle} icon={<NavigationChevronLeft/>} onTouchTap={this.prevPage} />
<FlatButton style={flatButtonStyle} icon={<NavigationChevronRight/>} onTouchTap={this.nextPage} />
Component Handlers
prevPage() {
const { page } = this.props.users;
this.props.loadUsers(page.number - 1);
}
nextPage() {
const { page } = this.props.users;
this.props.loadUsers(page.number + 1);
}
Connect
const mapStateToProps = state => {
return {
users: state.users
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
loadUsers: (page=0) => dispatch(loadUsers(page))
}
}
const Wrapped = connect(
mapStateToProps,
mapDispatchToProps
)(Users)
ONLY ONE ACTION
export const loadUsersOK = (result, page) => ({
type: LOAD_USERS_OK,
result,
page
})
May it helps.