React View is not updated with redux - reactjs

The react view is not updated (the render is never called) but the reducer was invoked.
I have the following:
1). the react view: I a field in the root state to determent if I need to show "TodoList" or "HousingInfo"
export default class RightPane extends React.Component{
static contextTypes = {
store: React.PropTypes.object
}
render(){
let store = this.context.store;
let curPage = store.getState()["mainRightPanePage"].currentPage;
return (
<div>
{(store.getState()["mainRightPanePage"].currentPage==="TodoList") && <TodoList/>}
{(store.getState()["mainRightPanePage"].currentPage==="HousingInfo") && <HousingInfo/>}
</div>
)
}
}
2). the action dispatching in another component
export default class LeftPane extends React.Component{
static contextTypes = {
store: React.PropTypes.object
}
handleink(pageId, e) {
e.preventDefault();
let store = this.context.store;
store.dispatch({'type':'switchPage', 'pageId':pageId});
...
}
3). the reducer: the following reducer was invoked
const mainRightPanePage = (state = {'currentPage':'TodoList'}, action) => {
switch (action.type) {
case 'switchPage':
return Object.assign({}, state, {
currentPage: action.pageId
})
default:
return state
}
}
export default mainRightPanePage
What did I miss?
thanks

In your example the RightPane component is not aware that Redux state was updated because you haven't subscribed to Redux state changes. You can subscribe to Redux store directly using subscribe method or you can connect your components to Redux store using connect method from React-Redux (recommended):
import {connect} from 'react-redux';
...
class RightPane extends React.Component{
...
render(){
let currentPage = this.props.currentPage;
return (
<div>
{(currentPage === "TodoList") && <TodoList/>}
{(currentPage === "HousingInfo") && <HousingInfo/>}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
currentPage: state.mainRightPanePage.currentPage
}
};
export default connect(
mapStateToProps
)(RightPane);

Related

React native context api not updated

I'm using RN NetInfo to check if user connected to internet using component <NetworkProvider /> and I want to pass this components stats to all screens and components in my app.
The problem is context api works good when I use it inside render function but when I use inside componentDidMount or componentWillMount the state not changed. Return initial value of isConnected state.
Please read comment in code
so this my code
NetworkProvider.js
import React,{PureComponent} from 'react';
import NetInfo from '#react-native-community/netinfo';
export const NetworkContext = React.createContext({ isConnected: true });
export class NetworkProvider extends React.PureComponent {
state = {
isConnected: true // initial value
};
componentDidMount() {
NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectivityChange);
}
componentWillUnmount() {
NetInfo.isConnected.removeEventListener('connectionChange', this.handleConnectivityChange);
}
handleConnectivityChange = isConnected => this.setState({ isConnected });
render() {
return (
<NetworkContext.Provider value={this.state}>
{this.props.children}
</NetworkContext.Provider>
);
}
}
this index.js
...
import { NetworkContext } from '../components/NetworkProvider';
export default class index extends Component {
static navigationOptions = {};
static contextType = NetworkContext;
constructor(props) {
super(props);
this.state = {
...
};
}
componentDidMount() {
// return object state but with inital value {isConnected :true}
console.log(this.context);
//this.fetchData(this.state.page);
}
render() {
// here when I use this.context return object {isConnected:true/false} depends on internet connection status on device
return(
<FlatList
...
/>
)
}
}
...

react redux props undefined on event listener when using mapDispatchToProps

My first time using react/redux and I'm trying to bind a simple action addClick to the 'click' event, but when I click I receive the error:
Uncaught TypeError: Cannot read property 'props' of undefined
My (stripped down) code is:
import {addClick} from './actions'
const mapDispatchToProps = {addClick}
class App extends Component {
componentDidMount() {
document.addEventListener('click', this.props.addClick)
}
componentWillUnmount() {
document.removeEventListener('click', this.props.addClick)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
Before I had it implemented without mapDispatchToProps using an action creator defined within the class and bound to this in the constructor. But I thought part of the point of mapDispatchToProps was to bind the action creator to this (as well as wrapping it in a dispatch)
What am I missing?
Thanks!
From what I can tell from the documentation, the object short-hand (const mapDispatchToProps =
{addClick}) you use for mapDispatchToProps doesn't bind this to anything. It just sees to it that your addClick action creator gets called with dispatch. So that if you, in your component execute addClick(3), then that will result in a call looking like this dispatch(addClick(3)).
I'm not sure why your action creator would need access to this though. Can't you just pass it what ever data it needs as a parameter? So that the call in your component might look like
componentDidMount() {
const {addClick, someOtherProp} = this.props;
document.addEventListener('click', () => addClick(someOtherProp));
}
Do you use props inside addClick action?
Check this example:
import React from "react";
import { render } from "react-dom";
import { connect, Provider } from "react-redux";
import { createStore } from "redux";
function addClick(event) {
return {
type: "CLICK",
payload: `pageX: ${event.pageX} | pageY: ${event.pageY}`
};
}
const mapStateToProps = state => {
return {
clickXY: state
};
};
const mapDispatchToProps = { addClick };
class App extends React.Component {
componentDidMount() {
document.addEventListener("click", this.props.addClick);
}
componentWillUnmount() {
document.removeEventListener("click", this.props.addClick);
}
render() {
return (
<h1>
Click message: {this.props.clickXY}
</h1>
);
}
}
function clickReducer(state = "None", action) {
switch (action.type) {
case "CLICK": {
return action.payload;
}
default:
return state;
}
}
let store = createStore(clickReducer);
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
class Root extends React.Component {
render() {
return (
<Provider store={store}>
<AppContainer />
</Provider>
);
}
}
render(<Root />, document.getElementById("root"));
Link to editor
So bind works well in this code.

React / Redux wait for store to update

I have a problem that a react component is rendering before the redux store has any data.
The problem is caused by the React component being rendered to the page before the existing angular app has dispatched the data to the store.
I cannot alter the order of the rendering or anything like that.
My simple React component is
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {addBot} from './actions';
class FlowsContainer extends React.Component {
componentDidMount() {
this.props.initStoreWithBot();
}
render() {
// *** at this point I have the store in state prop
//but editorFlow array is not yet instanced, it's undefined
const tasks = this.props.state.editorFlow[0].flow.tasks
return (
<div>
Flow editor react component in main container
</div>
);
}
}
const mapStateToProps = (state) => ({
state : state
})
const mapDispatchToProps = (dispatch) => {
return {
initStoreWithBot : () => dispatch(addBot("test 123"))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(FlowsContainer)
So how can I hold off the rendering until editorFlow array has elements ?
You can use Conditional Rendering.
import {addBot} from './actions';
class FlowsContainer extends React.Component {
componentDidMount() {
this.props.initStoreWithBot();
}
render() {
// *** at this point I have the store in state prop
//but editorFlow array is not yet instanced, it's undefined
const { editorFlow } = this.props.state;
let tasks;
if (typeof editorFlow === 'object' && editorFlow.length > 0) {
tasks = editorFlow[0].flow.tasks;
}
return (
{tasks &&
<div>
Flow editor react component in main container
</div>
}
);
}
}
const mapStateToProps = (state) => ({
state : state
})
const mapDispatchToProps = (dispatch) => {
return {
initStoreWithBot : () => dispatch(addBot("test 123"))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(FlowsContainer)
As far as I know, you can't.
the way redux works is that it first renders everything, then actions take place with some async stuff(such as loading data), then the store gets populated, and then redux updates the components with the new state(using mapStateToProps).
the lifecycle as I understand it is this :
render the component with the initial state tree that's provided when you create the store.
Do async actions, load data, extend/modify the redux state
Redux updates your components with the new state.
I don't think mapping the entire redux state to a single prop is a good idea, the component should really take what it needs from the global state.
Adding some sane defaults to your component can ensure that a "loading" spinner is displayed until the data is fetched.
In response to Cssko (I've upped your answer) (and thedude) thanks guys a working solution is
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {addBot} from './actions';
class FlowsContainer extends React.Component {
componentDidMount() {
this.props.initStoreWithBot();
}
render() {
const { editorFlow } = this.props.state;
let tasks;
if (typeof editorFlow === 'object' && editorFlow.length > 0) {
tasks = editorFlow[0].flow.tasks;
}
if(tasks){
return (
<div>
Flow editor react component in main container
</div>
)
}
else{
return null;
}
}
}
const mapStateToProps = (state) => ({
state : state
})
const mapDispatchToProps = (dispatch) => {
return {
initStoreWithBot : () => dispatch(addBot("test 123"))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(FlowsContainer)

React-Redux-Form save data to prop from onChange

I am new react-redux and I am trying to save to props the value entered in from a react-redux-form textbox by using the onChange event which can be passed to another component
My code snippet for the textbox is
<ListItemContent>
<Control component={Textfield} model="somemodel" label="MyLabel"
onChange={this.props}/>
</ListItemContent>
How can I save this value and make this available to other components?
EDIT I have it partly working:
<ListItemContent>
<Control component={Textfield} model="somemodel" label="MyLabel"
onBlur={this.onChangeOfValue}/>
</ListItemContent>
onChangeOfValue = (event) =>
{
this.setState({ newValueToPassAlong: event.target.value}); //newValueToPassAlong is set in constructor
};
.....
let mapStateToProps = (state) => {
return {newValueToGive: state.newValueToPassAlong} //This is undefined
};
export default connect(mapStateToProps)(form)
Further, my componentWillReceiveProps(nextProps) is not being fired when the other component's state changes.
// YOUR TEXTFIELD COMPONENT
import React, { Component } form 'react';
import { reduxForm, Field } from 'redux-form';
import {passValueToOtherComponent} from '../actions/your-actions-index-file';
import { connect } from 'react-redux';
import ListItemContent form 'list-item-content';
class TextField extends Component {
constructor(props) {
super(props);
this.state = {
textFieldValue: '',
}
this.onInputChange = this.onInputChange.bind(this);
}
onInputChange(event) {
var newValue = event.target.value;
this.setState({textFieldValue: newValue});
//when input changes
//call action to update global state...
this.props.passValueToOtherComponent(this.state.textFieldValue)
}
render() {
<form>
<ListItemContent>
<Control component={Textfield} model="somemodel" label="MyLabel"
onChange={this.onInputChange} value={this.state.textFieldValue}/>
</ListItemContent>
</form>
}
}
//ReduxForm wrapper
const wrappedReduxForm = connect(null, {passValueToOtherComponent})(TextField)
export default reduxForm({
form: 'TextField'
})(TextField)
// actions/your-actions-index-file.js
//create an action which will call to update global state
export const NEW_VALUE = "NEW_VALUE"
export function passValueToOtherComponent(value) {
return {
type: CREATE_POST,
payload: value,
}
}
//YOUR NewValue Reducer
//reducer_new_value.js
//create reducer which will accept payload data
import {NEW_VALUE} from '../actions/your-actions-index-file';
const INITIAL_STATE = {
valueToPass: null
};
export default function (state = [], action) {
console.log('Action...' action);
switch (action.type) {
case NEW_VALUE:
return { ...state, valueToPass: action.payload.data}
break;
default:
}
}
//Your Root Reducer
//Because you may have lots of state to manage, a rootReducer is awesome in managing it all
import { combineReducers } from 'redux';
import NewValueReducer from './reducer_new_value';
const rootReducer = combineReducers({
// state: (state = {}) => state
value: NewValueReducer,
});
export default rootReducer;
//finally pass this desired value to your desired Component
import React, {Component} from 'react';
import { connect } from 'react-redux';
class OtherComponent extends Component {
render() {
return (
<div>
<input value= {this.props.texFieldValue}>
</div>
)
}
}
function mapStateToProps(state) {
return { textFieldValue: state.value.valueToPass }
}
export default connect(mapStateToProps)(OtherComponent);
This is something I just typed out. Not sure if it will work, but it covers actions, reducers, and updating values from one component to anohter. Of course, this is a crazy way to do it using react redux. I'm not sure how efficient it would be to call this action on every input change. You might be better off passing the current value of state as a prop to your desired component.
If you have some more questions, I'd be happy to help or point you to some other resources.

Pattern for managing multiple reducers inside of a module [Redux]

I am new to Redux and am trying to figure out a scaleable way to setup my projects folder/file structure.
Lets say we have a file structure that looks like this:
root/modules/Todos/reducers
In the root of the project there lives a 'rootReducer.js' file which utilizes 'combineReducers()' to create a top-level implementation of the state tree:
[rootReducer.js]
import { combineReducers } from 'redux';
import todos from './modules/Todos/reducers/index.js';
export default combineReducers({
todos: todos
});
Inside of the 'reducers' folder for each module there are multiple reducers:
[root/modules/Todos/reducers]
>index.js
>Todos__addItem
>Todos__removeItem
The 'index.js' file imports all of the reducers for that module and exports a single object:
[index.js]
import { combineReducers } from 'redux';
import addItem from './Todos__addItem.js';
import removeItem from './Todos__removeItem.js';
export default const todos = combineReducers({
addItem: addItem,
removeItem: removeItem
});
Is this the correct use of 'combineReducers()'?
Does this pattern make sense when developing a large scale application?
What are (if any) potential pitfalls that come along with this pattern?
Thanks!
It's definitely not the correct usage of combineReducers. combineReducers is used to delegate management of a specific slice of state to a given function. Your example would actually create slices of state named addItem and removeItem, when what you really want to do is to update the same todos slice of state using those functions in different ways depending on which action was dispatched.
The Redux docs section on "Structuring Reducers" has some information that should help with this, including the section on Using combineReducers.
example from https://github.com/suin/redux-multiple-reducers-example
import {counter1, counter2 } from "../../reducers/index"
import CounterApp from "../containers/CounterApp";
const rootReducer = combineReducers({
one:counter1 ,
two:counter2
});
const store = createStore(rootReducer);
class App extends React.Component{
render() {
return (
<Provider store={store}>
<CounterApp />
</Provider>
);
}
Counter1 view
import * as counter1Actions from "../../actions/counter1Actions";
#connect(state => ({
counter1: state.one
}))
export default class Counter1 extends React.Component{
static propTypes = {
counter1: PropTypes.number.isRequired
}
componentDidMount() {
console.info("counter1 component did mount.");
}
onClick() {
console.info("counter1 button was clicked.");
const action = bindActionCreators(counter1Actions, this.props.dispatch);
action.increment();
}
render() {
return (
<div>
<h1>Counter 1</h1>
<button onClick={::this.onClick}>increment</button>
<div>Total: <span>{this.props.counter1}</span></div>
</div>
);
}
}
Counter2 view
import * as counter2Actions from "../../actions/counter2Actions";
#connect(state => ({
counter2: state.two
}))
export default class Counter2 extends React.Component {
static propTypes = {
counter2: PropTypes.number.isRequired
}
componentDidMount() {
console.info("counter2 component did mount.");
}
onClick() {
console.info("counter2 button was clicked.");
const action = bindActionCreators(counter2Actions, this.props.dispatch);
action.increment();
}
render() {
return (
<div>
<h1>Counter 2</h1>
<button onClick={::this.onClick}>increment</button>
<div>Total: <span>{this.props.counter2}</span></div>
</div>
);
}
}
CounterApp
import Counter1 from "../components/Counter1";
import Counter2 from "../components/Counter2";
class CounterApp extends React.Component{
render() {
return (
<div>
<Counter1/>
<Counter2/>
</div>
);
}
}
reducer
export default function counter1(state = initialState, event) {
switch (event.type) {
case "COUNTER1_INCREMENTED":
console.info(`counter1 ack ${event.type}: event =`, event);
return state + 1;
default:
console.warn("counter1 ack unknown event: state =", state, "event =", event);
return state;
}
export default function counter2(state: Object = initialState, event: Object): Object {
switch (event.type) {
case "COUNTER2_INCREMENTED":
console.info(`counter2 ack ${event.type}: event =`, event);
return state + 1;
default:
console.warn("counter2 ack unknown event: state =", state, "event =", event);
return state;
}
}

Resources