Issues with initialising Redux in React while using mapDispatchToProps - reactjs

I'm trying to learn some redux. Component I'm working on is a simble <div> based button that - when clicked - passes value as a parameter to the dispatch, so it can be displayed later someplace else.
After following both documentation and tutorials over the web I came up with the following code:
main app container
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import configureStore from './../store/configureStore.js'
import Input from './input.js'
let store = configureStore()
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Input />
</Provider>
)
}
}
button container
import React, { Component } from 'react'
import { connect } from 'react-redux'
import printOut from './../actions/actions.js'
import InputComponent from './../components/inputComponent.js'
import PropTypes from 'prop-types'
const mapDispatchToProps = (dispatch) => {
return {
onClick: (input) => dispatch(printOut(input))
}
}
const Input = connect(mapDispatchToProps)(InputComponent)
export default Input
button component
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Input extends Component {
render() {
return (
<div style={style} onClick={this.props.onClick('lala')}>Button!</div>
)
}
}
Input.PropTypes = {
onClick: PropTypes.func.isRequired
}
const style = {
height: 30,
width: 100,
backgroundColor: '#ff4068'
}
export default Input
Application breaks. I got this from the console:
Uncaught TypeError: (0 , _actions2.default) is not a function
at Object.onClick (index.js:33683)
at Input.render (index.js:33743)
(...)
index.js:22443 The above error occurred in the <Input> component:
in Input (created by Connect(Input))
in Connect(Input) (created by App)
in Provider (created by App)
in App
From what little I understood, there are some issues with button component and the way I'm trying to pass the param to props. So I tried to change it a little and added a function to handle that before render.
...
onClick(input) {
return this.props.onClick(input)
}
render() {
return (
<div style={style} onClick={onClick('lala')}>Button!</div>
)
}
...
The error I get this time is onClick is not defined. Oh, ok. I forgot this keyword before calling this new function. So I add it to the component and now I have
<div style={style} onClick={this.onClick('lala')}>Button!</div>
But the error being returned didn't really changed - it's again the original error of Uncaught TypeError: (0 , _actions2.default) is not a function
I'm starting to run out of ideas now. Could someone please tell me how what my be the issue here?
Help me Stack Overflow, you're my only hope! to quote timeless classic.

Are you sure you are importing printOut in properly? Shouldn't it be import { printOut } from './../actions/actions.js' ?
Then, first argument in connect is mapStateToProps and the second is mapDispatchToProps this is probably why you have dispatch is not a function.

You are importing InputComponent:
import InputComponent from './../components/inputComponent.js'
but inside button component you are exporting it as Input:
export default Input
so change InputComponent with :
import Input from './../components/inputComponent.js'
Use this for connect
export default connect(mapDispatchToProps)(Input)

You are facing 2 problems.
1. Syntax problem in your import
The following problem Uncaught TypeError: (0 , _actions2.default) is not a function is caused by the import of your actions.
Instead of
import printOut from './../actions/actions.js'
It should be
import { printOut } from './../actions/actions.js'
2. You are incorrectly using connect
connect accepts these two arguments with the following order:
mapStateToProps: contains the props you want to give to the component
mapDispatchToProps: contains the actions to dispatch
Even if you could call your action, there is no way the dispatch will happen because you call the reducers instead of the dispatch.
What you should do is the following:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { printOut } from './../actions/actions.js';
import InputComponent from './../components/inputComponent.js';
import PropTypes from 'prop-types';
const mapDispatchToProps = (dispatch) => {
return {
onClick: (input) => dispatch(printOut(input))
}
}
export default connect(null, mapDispatchToProps)(InputComponent);
You can read this documentation for more details: http://redux.js.org/docs/basics/UsageWithReact.html#implementing-container-components

Related

Simple passing of Value from one Component to another through context

I'm new to react native and would like to use Context to keep a socket connection alive between screens in the future. For now, I tried to learn the concept of context just to pass simple values around but the value doesn't get sent.
Tried to follow the tutorial here, but by sending simple values instead.
I create my ValueContext in ValueContext.js here.
import React from 'react';
const ValueContext = React.createContext();
export default ValueContext;
Here's my LoginScreen.js where I set context provider.
import React, { Component } from 'react';
import ConnectionScreen from './ConnectionScreen';
import ValueContext from './ValueContext';
const testValue = 5;
export const sendValue = props => (
<ValueContext.Provider value={testValue}>
<ConnectionScreen />
</ValueContext.Provider>
)
class LoginScreen extends Component {
render() {
return()
}
}
Then in my ConnectionScreen.js
import React, { Component } from 'react';
import { View, Alert } from 'react-native';
import LoginScreen from './LoginScreen';
import ValueContext from './ValueContext';
export const receiveValue = props => (
<ValueContext.Consumer>
{testValue => <ConnectionScreen {...props} testValue={testValue} />}
</ValueContext.Consumer>
)
class ConnectionScreen extends Component {
showAlertValue = () => {
Alert.alert(this.props.testValue);
}
render() {
return(
<View>
{this.showAlertValue()}
</View>
)
}
}
So after setting the value in LoginScreen, I would like to access it in ConnectionScreen. All I get in my alert box is an empty box with no values. Am I doing something wrong here?

React/Redux/Immutable - confusion regarding strategy for HOC with child component

I hope I can make sense of all of this in question format. I have been trying to work my way through this was the past 7 or so hours without success. My brain has simply run dry at this point and I am at a dead end regardless.
I am working with react 15.6.1, redux 3.7.1, react-redux 5.0.5, redux-immutable 4.0.0, redux-form 7.2.0, react-select 1.2.1.
My application has search functionality with field A and field B using 2 different forms (different forms for different pages). I don't it is pivotal to this question but I am using redux-form and react-select for the forms and search fields. I am storing user-entered search criteria in my Search reducer to synchronise autopopulation etc of select lists across the different forms.
redux-form---->react-select(field A)
---->react-select(field B)
My Search reducer is using Immutable.fromJs() in the initialState(). The reducer is working as expected. The problem I have is where to use the HOC in order to convert the Map object returned from the reducer into the JS array required for the react-select component.
MainSearchForm.js: -
import React, {Component} from 'react'
import { Field, reduxForm } from 'redux-form'
import { connect } from 'react-redux'
import FormFieldA from './FormFieldA'
import FormFieldB from './FormFieldB'
class MainSearchForm extends Component {
render() {
return(
<form>
<FormFieldA options={this.props.fieldAoptions}/>
<FormFieldB options={this.props.fieldBoptions}/>
</form>
)
}
}
function mapStateToProps ({Search}, props) {
return {
fieldAoptions: Search.get('fieldAoptions'),
fieldBoptions: Search.get('fieldBoptions'),
}
}
MainSearchForm = connect(mapStateToProps,{})(MainSearchForm);
export default reduxForm({
form: 'main-search',
})(MainSearchForm)
To simply the example, both FormFieldA and FormFieldB components follow as the same: -
import React, {Component} from 'react'
import Select from 'react-select';
class FormFieldA extends Component {
render() {
const handleOnChange = (value) => {
// call dispatch here
}
return(
<Select
name="field-a-input"
id="field-a"
options={this.props.options}
onChange={handleOnChange}
/>
)
}
}
export default FormFieldA
So the options prop in the react-select component must be a JS array: -
options: [
{ label: 'Red' },
{ label: 'Green' },
{ label: 'Blue' }
]
I can convert this using Immutable.toJS() but the Redux official guide recommends against this for performance reasons, recommending this pattern using an (I assume reusable) HOC component to parse the Immutable Map to the JS array.
My question is, how would I incorporate this? As you can see in the code above, right now my MainSearchForm is connecting to the Redux store to retrieve the options data required as options for the react-select options props. Would the answer be to just not have MainSearchForm and instead have a intermediary component for each field rendered by MainSearchForm, with this intermediary component call the HOC before using connect, as is seen in the guide: -
HOC from the Redux Immutable guide: -
import React from 'react'
import { Iterable } from 'immutable'
export const toJS = WrappedComponent => wrappedComponentProps => {
const KEY = 0
const VALUE = 1
const propsJS = Object.entries(
wrappedComponentProps
).reduce((newProps, wrappedComponentProp) => {
newProps[wrappedComponentProp[KEY]] = Iterable.isIterable(
wrappedComponentProp[VALUE]
)
? wrappedComponentProp[VALUE].toJS()
: wrappedComponentProp[VALUE]
return newProps
}, {})
return <WrappedComponent {...propsJS} />
}
Example intermediary smart component to parse the Immutable Map through the HOC and connect() with FormFieldA: -
import { connect } from 'react-redux'
import { toJS } from './to-js'
import FormFieldA from './FormFieldA'
function mapStateToProps ({Search}, props) {
return {
fieldAoptions: Search.get('fieldAoptions'),
}
}
export default connect(mapStateToProps)(toJS(FormFieldA))
Would that be best practice?
I would sincerely appreciate any help with this. This is my first time working with HOC & Immutable and it's a lot to take in. But I think I have finally got a grasp of the paradigm.
Don't worry too much about best practises, the best practises of today are the anti patterns of tomorrow. Don't get me wrong is good to know what's the "best" way doing things, but the best is always relative so take that into consideration.
The idea is that your FormFieldA and FormFieldB shouldn't care about redux, immutable, foobar, bajouras, wtvLibraryOrToolThatMightCome.
So your HOC, for the immutable should be in, your MainSearchForm:
import React, {Component} from 'react'
import { Field, reduxForm } from 'redux-form'
import { connect } from 'react-redux'
import { toJS } from './to-js'
import FormFieldA from './FormFieldA'
import FormFieldB from './FormFieldB'
class MainSearchForm extends Component {
render() {
return(
<form>
<FormFieldA options={this.props.fieldAoptions}/>
<FormFieldB options={this.props.fieldBoptions}/>
</form>
)
}
}
function mapStateToProps ({Search}, props) {
return {
fieldAoptions: Search.get('fieldAoptions'),
fieldBoptions: Search.get('fieldBoptions'),
}
}
MainSearchForm = connect(mapStateToProps,{})(toJS(MainSearchForm));
export default reduxForm({
form: 'main-search',
})(MainSearchForm)
Leave your FormFieldA and FormFieldB as they are, very dumb and just waiting for a simple array of options.
This is just a personal taste, but I usually even separate the component from the redux stuff.
For example with your case I would have MainSearchForm and a MainSearchFormContainer
import React, {Component} from 'react'
import FormFieldA from './FormFieldA'
import FormFieldB from './FormFieldB'
class MainSearchForm extends Component {
render() {
return(
<form>
<FormFieldA options={this.props.fieldAoptions}/>
<FormFieldB options={this.props.fieldBoptions}/>
</form>
)
}
}
export default MainSearchForm
And the container:
import { Field, reduxForm } from 'redux-form'
import { connect } from 'react-redux'
import MainSearchForm from './MainSearchForm'
import { toJS } from './to-js'
function mapStateToProps ({Search}, props) {
return {
fieldAoptions: Search.get('fieldAoptions'),
fieldBoptions: Search.get('fieldBoptions'),
}
}
const MainSearchFormContainer = connect(mapStateToProps,{})(toJS(MainSearchForm));
export default reduxForm({
form: 'main-search',
})(MainSearchFormContainer)

react-async-poll with a connected component

Looking at the docs for react-async-poll I'm following the Usage example to integrate asyncPoll into my component, but I'm getting a Uncaught TypeError: dispatch is not a function complaint from within my onPollinterval function
import React, { Component } from 'react';
import { connect } from 'react-redux';
import asyncPoll from 'react-async-poll';
import { fetchCaCities, } from '../actions';
import MyMap from './my-map';
class CaliforniaMap extends Component {
componentDidMount() {
this.props.fetchCaCities();
}
render() {
return (
<div>
<h1>California Map</h1>
<MyMap center={[37.5, -120]} zoom={6} layers={[this.props.caCities]} />
</div>
);
}
}
const onPollInterval = (props, dispatch) => {
console.log(dispatch); // undefined
return dispatch(fetchCaCities());
};
const mapStateToProps = state => ({
caCities: state.map.california.caCities,
});
export default asyncPoll(60 * 1000, onPollInterval)(connect(
mapStateToProps, { fetchCaCities }
)(CaliforniaMap)
Maybe react-async-poll doesn't work for connected components?
According to the docs:
The dispatch parameter is only passed to [onInterval] if it is
available in props, otherwise it will be undefined.
The example they give is confusing because it does not define dispatch anywhere, but they show onPollInterval using it.

action passed down from connect decorator is initially undefined

I have the following smart component and I am specifying the initialUpload function is a required func:
import React, { Component, PropTypes } from 'react';
import UploadForm from '../../components/UploadForm/UploadForm';
import HeaderSelection from '../../components/HeaderSelection/HeaderSelection';
import { initialUpload } from '../../redux/modules/Upload';
import { connect } from 'react-redux';
console.log(typeof initialUpload); //function
#connect((state) => {
return {
file: state.getIn(['upload', 'file'])
};
}, {
initialUpload
})
export default class Home extends Component {
static propTypes = {
initialUpload: PropTypes.func.isRequired
};
render() {
return (
<div>
<UploadForm handleFilesChange={this.props.initialUpload}/>
<HeaderSelection/>
</div>
);
}
}
But I get the error message:
warning.js:36Warning: Failed prop type: The prop initialUpload is
marked as required in Connect(Home), but its value is undefined.
The function is wrapped in dispatch and passed down the component hierarchy fine so I am confused as to what is going on.
#connect is assigning the static propTypes to the resulting rapping component and not to Home itself, which is rather a weird behaviour! I think it is caused by ES7 transformation, this behaviour was not present in previous versions
A work around is to define the propTypes on the wrappedComponent outside class definition:
#connect(...)
export default Home extends Component{
render() {
// ....
}
}
Home.WrappedComponent.propTypes = {
initialUpload: PropTypes.func.isRequired
}
another option would be not to use the decorator & use connect directly

How to assert if an action creator has been called from a redux connected container component

I've tried various ways but I can't seem to find anything about specifically testing if an action creator is called from a component. The hard part seems to be getting enzyme and redux connected containers to work. Below I'm exporting both connected and component class for tests. I tried wrapping the connected one in a provider but then I don't know how to target the textarea from enzyme to trigger the simulated onChange. The below works but I'm not sure if it's the best way of doing this?
Simple container:
I want to assert that onChange will call sendCommandValueChange with the correct value.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { sendCommandValueChange } from 'actions';
export class RobotCommand extends Component {
handleCommandChange(e){
this.props.sendCommandValueChange(e.target.value);
}
render(){
return (
<div>
<textarea
onChange={ e => this.handleCommandChange(e)}
/>
</div>
);
}
}
export default connect(null, { sendCommandValueChange })(RobotCommand);
and here is the test:
import React from 'react';
import { expect } from 'chai';
import sinon from 'sinon';
import { shallow, mount } from 'enzyme';
import { RobotCommand } from './RobotCommand';
describe('RobotCommand', () => {
it('should call sendCommandValueChange action creator on value change', () => {
const props = { sendCommandValueChange: sinon.spy() };
const wrapper = shallow(<RobotCommand {...props} />);
wrapper.find('textarea').simulate('change', {target: {value: 'foo'}});
expect(props.sendCommandValueChange.calledWith('foo')).to.equal(true);
});
});

Resources