Subscribe the state - Redux - reactjs

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.

Related

Does Redux update the React component it is connected to automatically?

I was under the impression that when my Redux store gets updated (via dispatching an action to the reducer), my Provider should make the new store available to all it's child components. So when I connect a container component to the store by using mapStateToProps(), that component should re-render when it receives the new props that go along with the store being updated, without the need for componentWillReceiveProps(). Am I wrong in thinking that?
After several hours of reading docs and other stack overflow answers, something just isn't clicking for me. I'm pretty sure my reducer is working correctly and is returning a new object, my components have access to the store, and my initial state is rendering just fine. If anyone could give me a better idea about the flow of things, I would be forever grateful.
Basically, I have a header component that imports the store, uses Provider and renders a "FAQ" component:
import React from 'react';
import FAQ from './Faq';
import {Provider} from "react-redux";
import store from '../store/index'
class Header extends React.Component {
render() {
return (
<Provider store = {store}>
<FAQ />
</Provider>
)
}
}
export default Header;
The "FAQ" component is a container that is connected to the store via mapStateToProps(), it imports the "FAQTest" component, which is a presentational component that will get passed this.state.thisFood as props so that it can render the data from the store. There is also an "addFood" function that dispatches my action, which can be seen in mapDispatchToProps() at the bottom.
import React from 'react';
import {connect} from 'react-redux';
import FAQTest from './faqTest';
class FAQ extends React.Component {
constructor(props) {
super(props);
this.state = {
thisFood: props.breakfast
};
}
//adding this makes the component state update, but I think it should work without it
componentWillReceiveProps(nextProps){
this.setState({thisFood: nextProps.breakfast})
}
addFood = () => {
this.props.addFood();
}
render() {
return (
<React.Fragment>
<button onClick={this.addFood}> Add Food </button>
<FAQTest food = {this.state.thisFood} />
</React.Fragment>
)
}
}
const mapStateToProps = function(state) {
return {
breakfast: state.faq.breakfast
}
}
function mapDispatchToProps(dispatch) {
return {
addFood: () => dispatch({type: 'ADD_FOOD', food: 'Waffles'})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(FAQ);
When I click the "Add Food" button, my store gets updated, and the props of my FAQ container component get updated, because of mapStateToProps(). I thought this would trigger my FAQ component to update its state, however the state does not get updated unless I use componentWillReceiveProps. Is this working as expected?
Just in case I'm doing something silly, here is my reducer:
const initialState = {
breakfast: ["eggs", "bacon"]
}
export default function faqReducer(state = initialState, action) {
switch (action.type) {
case "ADD_FOOD":
return Object.assign({}, state, {
breakfast: [...state.breakfast, action.food]
})
default:
return state;
}
}
Here is my root reducer with my combineReducers() function:
import { combineReducers } from "redux";
import faq from './faqReducer'
export default combineReducers({
faq: faq
});
The problem is that you're copying data from props to state, but only doing that when the component is mounted, and then expecting the state to somehow be updated when new props arrive.
Copying data from props to state is almost always the wrong approach. Please don't do that. Just use the value from props.
Two additional suggestions for improving the code:
Prefer using the "object shorthand" form of mapDispatch, rather than writing it as a function
We recommend using our new official Redux Starter Kit package as the standard way to write your Redux logic. It includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, and even creating entire "slices" of state at once.

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.

Retrieving a Redux state, but also change another Redux state in same React Native component?

I have a React component that currently just retrieves a state from Redux. Here is the general layout:
const mapStateToProps = state => {
return { stuff: state.stuff };
};
class MyComponent extends React.Component {
// use 'stuff' from redux to build the Views
}
export default connect(mapStateToProps)(MyComponent);
But now, what if I want to add a button that changes another Redux state called other?
To save the new Redux state, I know we have to create a dispatch to the action. ie,
const mapDispatchToProps = dispatch => {
....
};
Then finally connect them:
connect(null, mapDispatchToProps)(MyComponent);
But my confusion is if I am already connecting with mapStateToProps, how can I also map it to mapDispatchToProps so that I can update the Redux state in the same component?
You can use both ;-)
For example :
Action.js
export const textChanged = (newText) => {
return { type: "TEXT_CHANGED", newText }
};
HomeScene.js :
import { textChanged } from "../actions;
...
render () {
const { myText } = this.props;
<TextInput
value={myText}
onChangeText={(newText) => this.props.textChanged(newText)}
/>
}
function mapStateToProps(state) {
return {
myText: state.appContent.myText
};
}
export default connect(mapStateToProps, { textChanged })(HomeScene);
Reducer.js
case "TEXT_CHANGED":
return {
...state,
myText: action.newText
};
Hope it helps !
Hm, looks like I asked too early. I did a bit of reading and the parameters in connect() actually accepts both.
So like this:
connect(mapStateToProps, mapDispatchToProps)(MyComponent)

Dispatching an action from a Redux container without extending React.Component

I have a container component within my React and Redux application:
import { connect } from 'react-redux'
import MyComponent from '../components/mycomponent'
const mapStateToProps = state => ({
myData: state.myData[state.activeDataId]
})
export default connect(mapStateToProps)(MyComponent)
If state.myData[state.activeDataId] does not exist then I want to dispatch an action to fetchMyData or fetchMyDataIfNeeded.
Note that, at the moment, my container does not contain any JSX, it just forwards props to a presentational component. I have seen this being called a 'Pure Container' though I'm not sure if that's a common term.
Is there a common pattern to dispatch actions from a Pure Container? I am thinking without:
expecting the presentational component to worry about this logic by passing an onLoad event to it
making the container a React.Component and triggering via componentDidMount
Is it a bad idea to dispatch actions from mapStateToProps, mapDispatchToProps or mergeProps?
As noted elsewhere, doing this in the container is a bad idea.
Instead of worrying about this in the container, it makes sense to fetch the data conditionally in your component. I know you mentioned not wanting to extend react.component, but you should definitely consider making this component a class in order to fetch data in a component lifecycle hook.
As detailed in another answer, connect takes a second argument of mapDispatchToProps. Pass in the fetchData dispatcher there (example of how to do this here.)
Then, in your component you can check myData. If it is not there, then you dispatch via
this.props.whatYouCalledDispatch()
Yes, it is a bad idea to dispatch any action in container.
In your case, the best approach is:
Map your state, action creator to component props
Check the props in componentDidMount (or componentDidUpdate) and fetchDataYouNeed, then component will be updated
Your container should be:
import { connect } from 'react-redux';
import {fetchDataYouNeed} from './actions
import MyComponent from '../components/mycomponent';
const mapStateToProps = state => ({
myData: state.myData[state.activeDataId]
});
const mapDispatchToProps = (dispatch) => {
return {
fetchDataYouNeed: ()=>{
dispatch(fetchDataYouNeed());
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
Your component
class YourComponent extends Component{
componentDidMount(){
let {myData, activeDataId} = this.props;
if(myData && !myData[activeDataId]){
this.props.fetchDataYouNeed();
}
}
render(){
....
}
}
Learn more here https://facebook.github.io/react/docs/react-component.html#componentdidmount
This seems to work, though I'm not sure if it has any unintended effects:
import { connect } from 'react-redux'
import MyComponent from '../components/mycomponent'
import { fetchMyData } from '../actions/mydata'
const mapStateToProps = state => ({
dataId: state.activeDataId,
myData: state.myData[state.activeDataId]
})
const mapDispatchToProps = { fetchMyData }
const mergeProps = (stateProps, dispatchProps) => {
if (!stateProps.myData) {
dispatchProps.fetchMyData(stateProps.dataId)
}
return stateProps
}
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)
Alternatively, brianzinn suggested that by using Redux Saga to manage side effects, this issue becomes redundant.

How can I store and subscribe component state to redux store?

I have a container "HomeIndexView" and component "Table"
And I have both global and local component state of table.
Global table states are like below,
const initialState = {
allTables: [],
showForm: false,
fetching: true,
formErrors: null,
};
and local component state of table is like below,
componentWillMount() {
this.setInitialState();
}
setInitialState() {
this.setState({ tableBusy: false });
}
When a user logs in, in HomeIndexView, it shows all tables from data base through fetching.
So what I want to do is that connecting local component state to redux store so that when it changes state false to true, it changes background color of table. How should I connect local state to redux store? and should I create separate reducer and action for the local component's state?
Thanks in advance
--EDIT 1
import Table from '../../components/tables/table';
I am importing LocalComponent (Table) to HomeIndexView to show.
In my HomeIndexView, it renders all tables from database,
_renderAllTables() {
const { fetching } = this.props;
let content = false;
if(!fetching) {
content = (
<div className="tables-wrapper">
{::this._renderTables(this.props.tables.allTables)}
</div>
);
}
return (
<section>
<header className="view-header">
<h3>All Tables</h3>
</header>
{content}
</section>
);
}
_renderTables(tables) {
return tables.map((table) => {
return <Table
key={table.id}
dispatch={this.props.dispatch}
{...table} />;
});
}
render() {
return (
<div className="view-container tables index">
{::this._renderAllTables()}
</div>
);
}
The 'react-redux' library contains binding methods between React and Redux. If you haven't done so already, I really recommend checking out
Dan Abramov's: 'Getting into Redux' series of videos.
He goes into a good amount of detail about how to build a working Redux application from scratch and then how to do the same in conjunction with React (again from scratch).
He finalises on the use of the 'react-redux' helper library to make wiring up React with Redux easier.
The resulting solution for you would be to:
Use the connect method in Redux to create a Redux container component (just a term for a React component with Redux bindings)
mapStateToProps receives updates on the current state of the store which you can map to the target components props. Yo'd use this to get the current state of the store for use in your component
mapDispatchToProps which gets the store's dispatch action which you can use to bind action creators to (to update the store). You'd use this to connect the action creators that update the state of your store.
I assume you've already setup your reducers and actions. Now the only thing you need to do is dispatch an action from your LocalComponent.
Lets say you've a method called toggleTableState for updating the state tableBusy of your LocalComponent.
LocalComponent.js
import React, { PureComponent, PropTypes } from 'react';
import { connect } from 'react-redux';
import * as actions from './actions`;
#connect(
(state, props) => ({
isTableBusy: state.isTableBusy,
}),
{
toggleTableGlobalState: actions.toggleTableGlobalState,
})
export default class LocalComponent extends PureComponent {
static propTypes = {
toggleTableGlobalState: PropTypes.func,
isTableBusy: PropTypes.bool,
}
...
toggleTableState() {
this.setState({ tableBusy: !this.state.tableBusy });
this.props.toggleTableGlobalState(!this.state.tableBusy);
}
...
}
actions.js
export const TOGGLE_TABLE_STATE = 'TOGGLE_TABLE_STATE';
export function toggleTableGlobalState(tableState) {
return { type: TOGGLE_TABLE_STATE, tableState };
}
reducer.js
import { TOGGLE_TABLE_STATE } from './actions';
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
...
case TOGGLE_TABLE_STATE:
return { ...state, isTableBusy: action.tableState };
break;
...
}
}

Resources