What I Just want to fetch data from api and show it at frontend. I am using Redux to call the api using it's ACTIONS and REDUCERS. In Reducers i take the intialstate as empty array.When API is successfully called, I am updating store state.Below is the practical which can help to understand concept easily.
store.js
import { createStore } from 'redux';
import reducer from './reducers/reducer';
let store = createStore(reducer)
export default store
actions.js
import {
FETCH_IMAGES_SUCCESS
} from './actionTypes'
export function fetchImages() {
return dispatch => {
return fetch("https://api.com/data")
.then(res => res.json())
.then(json => {
dispatch(fetchImagesSuccess(json.posts));
return json.posts;
})
};
}
export const fetchImagesSuccess = images => ({
type: FETCH_IMAGES_SUCCESS,
payload: { images }
});
reducer.js
import {
FETCH_IMAGES_SUCCESS
} from '../actions/actionTypes'
const initialState = {
images:[]
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_IMAGES_SUCCESS:
return {...state,images:action.payload.images}
default:
return state
}
}
export default reducer;
Now, Please tell me what should i need to do to call that Redux action and
get Data from the API.I am using React to display data.
Thanks.
In React redux usage page you can use functions like mapStateToProps and connect to do that
You need a middleware like Redux-Saga or Redux-Thunk to talk with the actions and the global store maintained using Redux.
You may follow this Tutorial: https://redux.js.org/basics/exampletodolist
If you are going with Redux-Thunk, you need to modify your store assign like this:
const store = createStore(rootReducer, applyMiddleware(thunk));
Now, have a container to all the Parent component you have.
import { connect } from 'react-redux';
import App from '../components/App';
export function mapStateToProps(appState) {
return {
/* this is where you get your store data through the reducer returned
state */
};
}
export function mapDispatchToProps(dispatch) {
return {
// make all your action dispatches here
// for ex: getData(payload) => dispatch({type: GETDATA, payload: payload})
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
As Mustafa said you need to use mapStateToProps. Let me explain myself.
What you just done is just the configuration for the main store (there's only one in redux). Now you need to use it in your components, but how ? When you create a Component the content of the store will be passed as props with the help of Containers.
Containers are the way to link your store with your react component.
Said that, you need to install redux and react-redux. In your code above you have successfully configured the store with the reducers with redux library. Now you need react-redux to create the Container (which wraps your react component).
Here is an example of how to put this all together:
https://codepen.io/anon/pen/RqKyQZ?editors=1010
You need to use mapStateToProps similar to the code below. Let say your reducer is called test and it is part of a state.
const mapStateToProps = (state, props) =>
({
router: props.router,
test: state.test
});
Then test will be used as a property in a React class. Obviously you need to include respective imports for React.
Related
I'm implementing Redux on React and it's currently working, but I'm not sure if it's correct. I have a redux folder with 3 files: actions, reducers and store.
Actions has this code:
export const SETTER_USER = "SETTER_USER"
export const setterUserAction = ({ email, username, role, lastVideo }) => ({
type: SETTER_USER,
payload: { email, username, role, lastVideo }
})
I'm calling this action from the component, this is the code on the component:
import React, { useState, useEffect } from "react";
import { connect } from 'react-redux'
import { setterUserAction } from '../../redux/actions'
...
const Login = ({ navigation, user, setUser }) => {
...
}
const mapStateToProps = state => ({
user: state.user
})
const mapDispatchToProps = ({
setUser: setterUserAction
})
export default connect(mapStateToProps, mapDispatchToProps)(Login)
I used to have the action inside the component, which I can imagine is not ideal. So I moved it to an actions.js file. But now I'm not using a dispatch function, which feels weird as dispatch is part of the whole pattern. So, what do you think? Is this correctly implemented? Or it's just working by luck?
You are defining mapDispatchToProps as an object. It was recommended by react-redux team: You can read more about it in here: https://react-redux.js.org/using-react-redux/connect-mapdispatch#defining-mapdispatchtoprops-as-an-object
We recommend always using the “object shorthand” form of mapDispatchToProps, unless you have a specific reason to customize the dispatching behavior.
Update: If you are using Function component with Hook, you should use the React-Redux hooks API: https://react-redux.js.org/api/hooks
We recommend using the React-Redux hooks API as the default approach in your React components.
The existing connect API still works and will continue to be supported, but the hooks API is simpler and works better with TypeScript.
But if you are using Class Component. You should use connect with mapDispatchToProps as an object instead of as a function.
I am beginning with Redux and I always used it in components with connect() and mapStateToProps(), but now I want to call my API with setInterval() every x time to check if the server has new data not stored in the Redux store, and substitute it.
My approach was to create a function that reads the store and update it like that:
import { store } from './dir/dir/store.js'
const refresher = async () => {
const state = store.getState();
// Call API, compare 'state.calendar' with calendar from server
// Call store.dispatch() if they are different, then update Redux store
}
export default refresher
My questions are:
Is this a good practise to use Redux?
Is there a better approach to this problem?
Thanks
It's perfectly fine to export the store and use within a vanilla js/ts file.
Example Redux Store
Make sure to export the store that you create
import { configureStore } from "#reduxjs/toolkit";
import { slice } from "../features/counterSlice";
export const store = configureStore({
reducer: {
counter: slice.reducer
}
});
Usage in Non-Component Code:
You can then import the created store in any other code
import { store } from "../App/store";
import { slice as counterSlice } from "../features/counterSlice";
export function getCount(): number {
const state = store.getState();
return state.counter.value;
}
export function incrementCount() {
store.dispatch(counterSlice.actions.increment());
}
Traditional Usage in Functional Component
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "../App/store";
import { slice as counterSlice } from "../features/counterSlice";
export function Clicker() {
const dispatch = useDispatch();
const count = useSelector((state: RootState) => state.counter.value);
const dispatchIncrement = () => dispatch(counterSlice.actions.increment())
// ...
Example Slice
import { createSlice } from "#reduxjs/toolkit";
export const slice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1;
}
}
});
Demo in Codesandbox
Note: You cannot use this option with Server Side Rendering. If you need to support SSR, you can use middleware to listen to dispatched actions and handle elsewhere.
Further Reading
What is the best way to access redux store outside a react component? | Stack Overflow
Access the Redux Store Outside a React Component | Blog
How can I access the store in non react components? | Github Issues
Here you can access the store and action out side any component like index.js file in react-native.
import {updateLocationAlertItem} from './src/store/actions/locationAlertAction';
import {store} from './src/store/index';
store.subscribe(listener);
function listener() {
state = store.getState().locationAlertReducer;
}
store.dispatch(
updateLocationAlertItem({
index: index,
is_in_radius: true,
is_notification: true,
arrival_time: moment().format('DD/MM/YYYY hh:mm'),
exit_time: item.exit_time,
}),
);
What is the recommended way of accessing a variable from a different reducer?
import { createStore, combineReducers } from 'redux';
import mainReducer from './reducers/main';
import configReducer from './reducers/config';
const rootReducer = combineReducers({
main:mainReducer,
config:configReducer
});
const store = createStore(rootReducer);
export default store;
I have two different reducer and I have an action inside mainReducer where I want to access a variable inside configReducer. What is the proper way to do this with Redux?
export default function (state, action) {
switch (action.type) {
case "UPDATE_ACTIVE":
//need to check variable options inside configReducer
default:
return state;
}
}
Basically a reducer just returns a slice of the global state i.e store.
If you are using a middleware like thunk, you can share the data like so:
export function updateActive(params) {
return (dispatch, getState) => {
const { config } = getState(); // the part you want to access.
dispatch({
type: UPDATE_ACTIVE,
options: config.options,
params,
});
};
}
So you get all the data in the action itself and then dispatch with the ACTION_TYPE.
You can read more on sharing state between reducers on :
https://redux.js.org/faq/reducers/
https://redux.js.org/api/store#getstate
I am working on my very first react app, and I have successfully setup my Node API and MongoDB, and am now trying to integrate redux into my application. I will try to share the relevant code snippets here.
First, in my node API, I have a model mlb_ids_logos_colors.js with some baseball data. My react app is currently getting this data using the following fetch using an async function:
export async function get_mlb_ids_logos_colors() {
return fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json();
})
}
I would like to get the data from this endpoint into my redux store, and then from the redux store into the various components that will use it, but I am a bit stuck. As far as redux, I have the following files:
reducers/index.js (my rootReducer)
import { combineReducers } from 'redux';
import mlbIdsLogosColorsReducer from './mlb-ids-logos-colors-reducer';
export default combineReducers({
mlbIdsLogosColorsReducer
})
reducers/mlb-ids-logos-colors-reducer.js
export default function reducer (state={
mlbIdsLogosColorsData: [],
}, action) {
switch (action.type) {
case "FETCH_COLORS": {
return {...state, mlbIdsLogosColorsData }
}
}
return state;
}
actions/mlb-ids-logos-colors-action.js
export const FETCH_COLORS = "FETCH_COLORS";
export function fetchMlbIdsLogosColors() {
return function(dispatch) {
dispatch({type: "FETCH_COLORS"});
fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json();
})
}
}
lastly, I setup my store in **store.js* as follows, and import this into my apps main index.js file:
store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
Any help with this is appreciated. For reference, I am receiving the following error when launching the app:
./src/reducers/mlb-ids-logos-colors-reducer.js
Line 7: 'mlbIdsLogosColorsData' is not defined no-undef
I'm aware this is a quite-obvious error message, but admittidly I'm not sure where I should be defining mlbIdsLogosColorsData.
Thanks in advance!
EDIT: I don't have to make any changes ever to the data at my /mlb/mlb_ids_logos_colors endpoint. I just want to get this data into the redux store, and then from the redux store into the components. I know to use mapStateToProps() and connect() in my components to get the data into the components.
EDIT2: I HAVE DIFFERENT NAMES FOR THE ACTION! let me fix that, and see if that resolves the issue!
I'm in a hurry sorry if I misleading you but roughly you are dispatching an action without data. You should use something like that in your action creator:
export function fetchMlbIdsLogosColors() {
return function(dispatch) {
fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json()
.then( json => dispatch({type: "FETCH_COLORS", json}));
})
}
}
and then use this payload (json) in your reducer like that:
export default function reducer (state={
mlbIdsLogosColorsData: [],
}, action) {
switch (action.type) {
case "FETCH_COLORS": {
return {...state, mlbIdsLogosColorsData: action.json }
}
}
return state;
}
Again, this is a rough suggestion. I did not check your whole code. But you are getting undefined error since there is not a variable named mlbIdsLogosColorsData right now.
I'm trying to display some data which will always be updated but when I add some new data to store, the new data is not seen on the screen as I didn't know about subscribe to store method. But I don't know where to use it and how to use it. I couldn't find any suitable example for my project.
First possibility to use as I did search on it (use it like mapStateToProps);
const mapStateToProps = (state) => {
return {
dashboardsList: state.header.dashboardsList,
templatesList: state.header.templatesList
}
}
DashboardDropdown.propTypes = {
dashboardsList: PropTypes.array,
templatesList: PropTypes.array
};
export default connect(mapStateToProps, null)(DashboardDropdown);
Let's say I want to subscribe to state.header.templatesList, how can I write it?
Or should I subscribe the state.header.templatesList in the app-store.js?
This is my store class;
const RootReducer = (state = {}, action) => {
return {
[HeaderModule.constants.NAME]: HeaderModule.reducer(
state[HeaderModule.constants.NAME],
action
),
[AuthModule.constants.NAME]: AuthModule.reducer(
state[AuthModule.constants.NAME],
action
),
[DashboardViewModule.constants.NAME]: DashboardViewModule.reducer(
state[DashboardViewModule.constants.NAME],
action,
),
[TemplateViewModule.constants.NAME]: TemplateViewModule.reducer(
state[TemplateViewModule.constants.NAME],
action,
),
[WidgetContainerModule.constants.NAME]: WidgetContainerModule.reducer(
state[WidgetContainerModule.constants.NAME],
action
)
}
}
const Store = createStore(RootReducer, applyMiddleware(thunk, logger()));
export default Store;
If I should subsribe it here, how can I again write it?
Thanks a lot!
I think I can help you with this - you'll have to add some code to your components that will map the Redux state to that component's props.
First, install react-redux - $ npm install --save react-redux, if you haven't yet.
Something like:
MyComponent.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
const mapStateToProps = state => ({
state
});
class MyComponent extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
console.log(this.props.state)
}
render(){
return(
<div>Hello</div>
)
}
}
export default connect(mapStateToProps, undefined)(MyComponent);
When this loads up, you'll see that console.log(this.props.state) will refer to the Redux state, because we have mapped the state (as in the Redux state) to the props of the component. When Redux updates, that should 'subscribe' the component to those changes.
If DashboardDropdown (the default export of that file) is rendered on the DOM as of now, then you are now subscribed to the store. Whenever the global state (store) changes, every mapStateToProps in every ConnectedComponent will be invoked giving the component (DashboardDropdown) the new props.