How to connect component (file) to redux store - reactjs

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);

Related

Logic in component or mapStateToProps

If MyComponent gets data from the redux store, but organises it in some way first before mapping it, should that organisation be done in the component or mapStateToProps function and why?
const MyComponent = ({ data }) => {
// IN HERE?
return (
<div>
{data.map((d) => (...))}
</div>
);
};
const mapStateToProps = (state) => {
const output = state.data
// OR HERE?
return { data: output };
};
export default connect(mapStateToProps)(MyComponent);
Hello have a nice day.
i think is better have a file with all the logic to conect with redux, so every time i need to connect with redux i create a file that name is ComponentNameContainer.jsx, this file looks like that:
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import Row from '../components/Row';
import {doSomething} from '../redux/somethingActions'
// here the imports of function from your actions
export default withRouter(connect(
(state, ownProps) => {
return {
// props that u need from redux
// example: state.sessionReducer.user
}
},
{
// functions that u need from redux
//example: doSomething
}
)(Row))
i have a folder call containers to store all the container files to keep track of the components that are connected with redux.

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.

Dispatch is not available in this.props

I'm very new to React and trying to write an application which outputs a portfolio to one part of the page and, based on user interaction with that portfolio, displays some information in a lightbox/modal elsewhere in the DOM.
This requires that my two rendered components have some kind of shared state, and my understanding is that the best (or one of the best) way to achieve this is with Redux. However, being new to React and now adding Redux into the mix, I'm a little out of my depth.
I've created some (for now very dumb) action creators and reducers, all I'm trying to do initially is fetch some JSON and add it to my store. However, I'm not able to access dispatch from within my component and I'm not really sure where I'm going wrong.
If I console.log this.props from within my component I get an empty object, "{}".
Here are the main parts, any pointers would be really appreciated:
App.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import store from './redux/store';
import { Portfolio } from './redux/components/portfolio';
ReactDOM.render(
<Provider store={store}>
<Portfolio />
</Provider>,
document.getElementById('portfolioCollection')
);
actions/actionCreators.js:
export const populatePortfolio = obj => ({
type: POPULATE_PORTFOLIO,
obj
});
export const populateLightbox = obj => ({
type: POPULATE_LIGHTBOX,
obj
});
portfolio.js:
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../actions/actionCreators';
export class Portfolio extends React.Component {
componentDidMount() {
this.getPortfolioData();
}
getPortfolioData() {
fetch('/data.json')
.then( (response) => {
return response.json()
})
.then( (json) => {
// dispatch action to update portfolio here
});
}
render() {
return(
// render component
);
}
}
function mapStateToProps(state){
console.log('state', state);
return {
state: state
}
};
function mapDispatchToProps(dispatch) {
console.log('dispatch', dispatch);
return {
actions: bindActionCreators({ populatePortfolio: populatePortfolio }, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Portfolio);
this.props is empty because you have not passed any props. You are using the unconnected component instead of the one that has been connected to redux.
To fix this, replace this line:
import { Portfolio } from './redux/components/portfolio';
with
import Portfolio from './redux/components/portfolio';
You are exporting both the connected and the unconnected component. You probably only want the last export. Since the connected component is exported as default you import it without using {} deconstruction.
unless you need to import the unconnected component in tests or something like that, you can remove the export statement from this line, since it makes no sense to export something that you don't intend to import in another file.
export class Portfolio extends React.Component {
You aren't meant to manually call dispatch in your components. The action creator function is automatically bound to dispatch for you. Simply call this.props.populatePortfolio() in your component.

Subscribe the state - Redux

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.

Accessing redux store inside functions

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);
....
}

Resources