Accessing redux store inside functions - reactjs

I would prefer to have a function exposed from a .js file , within that function I would prefer to have access to the variables in the store.
Snippet of the code : -
import { connect } from 'react-redux';
function log(logMessage) {
const {environment} = this.props;
console.debug('environment' + environment + logMessage );
....
}
function mapStateToProps(state) {
return {
environment : state.authReducer.environment
};
}
export default function connect(mapStateToProps)(log);
I have many components, which attach the class through connect, can I attach functions through connect()?

Edit 1
Some_File.js
import store from './redux/store.js';
function aFunction(){
var newState =store.getState();
console.log('state changed');
}
store.subscribe(aFunction)
I am assuming you have created store and reducers as redux expects.
~~~~~~~~~~~~~~~
Original Answer Starts
This is a sort of hack, I don't know what you are doing so I can't say you should or you should not do it, but you can do it this way. I have copy-pasted some of your code with some modifications.
Class XYZ extends React.Component{
componentWillReceiveProps(props){
//in your case this.props.storeCopy is redux state.
//this function will be called every time state changes
}
render(){
return null;
}
}
function mapStateToProps(state) {
return {
storeCopy : state
};
}
export default function connect(mapStateToProps)(XYZ);
Put this component somewhere at top, may just inside provider, whenever state changes this componentWillReceiveProps of this component will be invoked.

If you have a pure functional component then you can access the redux state directly like this:
import store from './redux/store';
function getStoreDetails() {
console.log(store.getState());
}

The proper place to access the store is through a container, connect is used to connect a container to a component, you cannot connect a random function to it.
There is a logger middleware for redux that you might wan't to take a look at, it does what you're trying to achieve.
To use it, just pass it as a middleware to your store:
import createLogger from 'redux-logger';
const store = createStore(
reducer,
applyMiddleware(logger)
);
A more proper way to debug a redux app is to use React Dev Tools, if you use Chrome, I recommend you to use the React Dev Tools Extension. Just install it and use it as a middleware
let store = createStore(reducer, window.devToolsExtension && window.devToolsExtension());
With it, at any given moment you can see the whole state of your store, see the actions being fired and how they affect the store, and even rewind your application by un-doing actions.

Yes. You can attach functions via connect as below;
const mapDispatchToProps = (dispatch) => {
return {
testFunction: (param1) => dispatch(testFunction(param1)),
testFunction1: () => dispatch(testFunction1())
};
};
export default function connect(mapStateToProps, mapDispatchToProps)(log);

redux state can be accessed as prop in a function by using below format.
1:
import { connect } from 'react-redux';
// log accepts logMessage as a prop
function log(props) {
const { environment, logMessage } = props;
console.debug('environment' + environment + logMessage );
....
}
function mapStateToProps(state) {
return {
environment : state.authReducer.environment
};
}
export default connect(mapStateToProps)(log);
How to use log function?
log({ logMessage: "Error message" });
2:
// import redux store
import { store } from './Store';
function log(logMessage) {
const currentState = store.getState();
const environment = currentState.authReducer.environment;
console.debug('environment' + environment + logMessage);
....
}

Related

How to use Redux with React

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.

Get Redux Store In Presentational Component

This is a tagalong of this question here. In contrast to that question, I don't need my presentational component to be a class. Is there a way to retrieve Redux's store without using a class and the corresponding super() method?
container.js
function mapStateToProps(state) {
return {
a: state.a
};
}
function mapDispatchToProps(dispatch) {
return {
setA: a => dispatch(setA(a)),
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(container);
presentational.js
function b({ a }) {
return {
console.log(a) // returns undefined
}
}
Does the dispatch work the same way?
Yes you can access the store from anywhere you like. You just need to export it from the file where you create it.
import { configureStore } from '...';
export const store = configureStore(...);
Import the store in your presentational.js file
import {store} from '...'
// And now you can access the current state or perform a dispatch:
store.getState() // current state
store.dispatch()
EDIT
My previous answer was wrong apologies for that, actually functional components (the one without class) can also access redux state and they can do that using connect from react-redux the very same way class components do
Reason for previous wrong answer
I once long ago tried to use connect with functional components and it didn't work for some weird reasons but when I converted the functional component to class component it worked without making changes to any other logic so I concluded that only class components can access redux state.
But I was wrong as I tested my case in this sandbox link https://codesandbox.io/s/38yw3l6nom (please look out for sample component in containers folder)
Previous wrong answer (please don't read if you are looking only for the correct solution)
No, connect from 'react-redux' modules only works on class components. Also, super is a method called in a constructor and hence they can only be called in class. You can refer this link here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes.
If you want any data stored in redux in your presentational component you'll have to pass it through a container component which will have access to the redux store. Please read more about here https://redux.js.org/recipes/writing-tests#components.
If you want to use connect on a presentational component then you'll have to use composition. recompose to achieve that.
import {compose} from 'recompose';
const presentationalComponent = props => {
return (
<div>{//Your content here}</div>
);
}
function mapStateToProps(state) {
return {
a: state.a
};
}
function mapDispatchToProps(dispatch) {
return {
setA: a => dispatch(setA(a)),
};
}
export default compose(
connect(
mapStateToProps,
mapDispatchToProps
)
)(presentationalComponent);
You'll not have to make container component this way.

How to connect component (file) to redux store

I need to create component/file/class whatever and connect it to redux store.
I don't need this component to be rendered.
It's just a helper component which can contain methods that return value from store.
I tried to create a file with:
export function getKeyFromReduxStore(key:string) {...
but this is just a file that export function and I can't (or don't know) how to connect it to redux store.
All my components are connected with store throught:
<RouterWithRedux>
<Scene key="root">...
but as I said this is no scene it's just helper component that I want to reuse through whole app.
How can I make such a component and connect it to redux?
Redux store has a method called getState which gets the state of the store. You can import the store you have created in the file where redux store is required.
// in the file where you created your store
import { createStore } from 'redux';
export const myStore = createStore(
// ... some middleware or reducer etc
)
// in your component/file/class
import { myStore } from '../path/to/store'
export function getKeyFromReduxStore(key:string) {
return (myStore.getState())[key];
}
Alternatively, you can pass in the store to getKeyFromReduxStore function and call it in react component where store would be available. For instance in the mapStateToProps function:
// component/file/class
export function getKeyFromReduxStore(store, key) {
return store[key];
}
// react component with access to store
import { getKeyFromReduxStore } from '../path/to/file';
class MyKlass extends Component {
// ... your logic
render() {
const val = this.props.getKey(someVal);
}
}
const mapStateToProps = (state) => ({
getKey: (key) => getKeyFromReduxStore(store, key),
})
export default connect(mapStateToProps)(MyKlass);

Redux state value globally

I am having a helper class which provies info about the logged in user. I would like to have it as a static method and use it in several places ( which are not react components)
How to get access the redux store value in a class model ?
If you can manage this in your application it would be cleaner (and more testable) to pass it around with dependency injection (DI) as opposed to using a global variable, static or singleton. In a simple form you could just pass it in the constructor like this:
var store = createStore(...);
var app = new AppClass(store);
If you're not using a bundler like Webpack, Lukas Katayama's answer should work window.store = createStore(...) but if you are using one, you can also expose your redux store by exporting it and importing where you need it.
//store.js
export const store = createStore(...);
//other file
import { store } from './store.js';
const someFuncOrClass = (...) => {
const user = store.getState().userInfo;
}
One way could be set a global variable
window.store = createStore(...)
and use it, accessing window.store
Looks like a straight forward implementation. You can create a getter for store in the same file where you creatStore(...).
store.js
import { createStore } from 'redux';
let store = {};
export default function store(...) {
/*
* things to do before creating store
*/
store = createStore(...);
return store;
}
export function getStore() {
return store;
}
helper.js
import { getStore } from './store';
export function getUserInfo() {
const store = getStore();
const globalState = store.getState();
return globalState.userInfo;
}

Redux silently not rendering connect()ed component

I am writing an app using Redux and can't get a Redux connect()ed component to render at all.
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';
var store = createStore((s, a) => s, {hello: "WORLD"});
class App extends React.Component {
render() { return <h1> Hello, world! </h1>; }
}
var connectedApp = connect(function(s) { debugger })(App);
$(document).ready(function() {
var target = document.getElementById("root");
// DOES render
React.render(<App/>, target);
// Never renders
React.render(<connectedApp/>, target);
});
The app is using babel, babelify, redux and redux-react.
Returning an object inside of connect() did not seem to modify this.props in the component, either. The debugger statement passed to connect never fires.
Is there something wrong with this code? Why isn't the component rendering? Why does the debugger statement never fire?
JSX converts component types that start with a capital letter into React.createElement calls to that type:
<App/> // => React.createElement(App);
However, it converts component types that start with lowercase letters into DOM nodes (by passing it as a string instead of a reference):
<connectedApp/> // => React.createElement("connectedApp");
In fact, if you look at the DOM via your browser's inspector, you'll likely see
<connectedApp data-reactid=".0"></connectedApp>
Try capitalizing connectedApp:
var ConnectedApp = connect(...)(App);
// ...
React.render(<ConnectedApp/>, target);
You are not passing a valid ReactElement to your second render method.
The first <App/> component is valid an therefor is being rendered as a DOM node.
The second <connectedApp/> however is not a ReactElement. So it won't be rendered at all. It is just a function. var connectedApp = connect(function(s) { debugger })(App);
Taken from the API from REDUX the typical use of the connect function is as follows:
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(App)
with the arguments:
[mapStateToProps(state, [ownProps]): stateProps] (Function)
The App component subscribes to the redux store updates, and this function is always called if the component updates. The return of this function should be a object.
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function)
As object, every function inside will be recognized as a valid action creator
You don't need to pass the connect to a render method, just subscribe your App to the REDUX store.
So taken from the official REDUX page, this is how you set up the subscription:
import { React } from 'react'
import * as actionCreators from './actionCreators'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
class TodoApp extends React.Component {
//your App
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)`

Resources