Which way should I use for Connector in Redux? - reactjs

I seen 2 ways of doing the same thing but I am not sure what is the proper way.
Component
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {selectUser} from '../actions/index'
class UserList extends Component {
renderList() {
return this.props.users.map((user) => {
return (
<li
key={user.id}
onClick={() => this.props.selectUser(user)}
>
{user.first} {user.last}
</li>
);
});
}
render() {
return (
<ul>
{this.renderList()}
</ul>
);
}
}
// Get apps state and pass it as props to UserList
// > whenever state changes, the UserList will automatically re-render
function mapStateToProps(state) {
return {
users: state.users
};
}
// Get actions and pass them as props to to UserList
// > now UserList has this.props.selectUser
function matchDispatchToProps(dispatch){
return bindActionCreators({selectUser: selectUser}, dispatch);
}
// We don't want to return the plain UserList (component) anymore, we want to return the smart Container
// > UserList is now aware of state and actions
export default connect(mapStateToProps, matchDispatchToProps)(UserList);
https://github.com/buckyroberts/React-Redux-Boilerplate
Or
import React from "react"
import { connect } from "react-redux"
import { fetchUser } from "../actions/userActions"
import { fetchTweets } from "../actions/tweetsActions"
#connect((store) => {
return {
user: store.user.user,
userFetched: store.user.fetched,
tweets: store.tweets.tweets,
};
})
export default class Layout extends React.Component {
componentWillMount() {
this.props.dispatch(fetchUser())
}
fetchTweets() {
this.props.dispatch(fetchTweets())
}
render() {
const { user, tweets } = this.props;
if (!tweets.length) {
return <button onClick={this.fetchTweets.bind(this)}>load tweets</button>
}
const mappedTweets = tweets.map(tweet => <li>{tweet.text}</li>)
return <div>
<h1>{user.name}</h1>
<ul>{mappedTweets}</ul>
</div>
}
}
https://github.com/learncodeacademy/react-js-tutorials/tree/master/5-redux-react
The first way uses 2 different functions mapStateToProps() and matchDispatchToProps() while the other way uses #connect(....).
When I use the #connect I get a whole bunch of warnings saying that it has not been finalized and might change.

The # symbol is a decorator which is still considered experimental. So I would use that at your own risk. Your first code block is the safer way to do it as described in the official docs. Both blocks essentially do the same thing but decorators are more sugar than anything.
References:
https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options
What's the '#' (at symbol) in the Redux #connect decorator?

I think the first method will give you less problems in the end. Someone else can chime in though too.

The answer by Jackson is right in every sense however he is missing out the importance of using the first version for the usage of unit testing. If you want to be able to unit test a component (which usually means testing with the unconnected version) you need to be able to export the connected and unconnected component.
Using your example and assuming you are using jest/enzyme you could do something like this:
// notice importing the disconnected component
import { UserList } from '../relative/file/path/UserList'
import { mount } from 'enzyme'
describe('UserList', () => {
it('displays the Username', () => {
const users = [{fist: 'Person', last: 'Thing'}, ... ]
const UserList = mount(<UserList users={users} />)
export(UserList.find('li')[0].text()).toEqual('Person Thing')
});
});
Once you build larger projects being able to unit test will provide sanity to your coding life. Hope this helps

Related

What is the proper interface for element with "GetWrappedInstance"

I am writting React+Redux with typescript. I need to access the reference of one wrapped instance, like this.refs.items.getWrappedInstance(), but got typescript error Property 'getWrappedInstance' does not exist on type 'ReactInstance'
Which interface shall I give to items? Are there any interface defined by Redux that I can declare or I need to create my own?
I tried to google and search stackoverflow but cannot find an answer.
Thank you!
We got the same error using Flow and our most senior UI developer ultimately said to use // $FlowIgnore: some comment
I think that because accessing wrappedInstance is an antipattern flow and typescript may not support it...yet?
I am interested if anyone has a different answer.
I came across this issue and while I didn't find a native solution, I did manage to create one. Below is a working sample implementation with the solution.
Keep note of the { withRef: true } in the connect function.
Here is a small utility type to add the missing definition.
// connect.ts
type WrappedConnectedComponent<T> = {
getWrappedInstance(): T
}
export function unwrapConnectedComponent<T>(component: T): T | undefined {
if (component) {
const wrappedComponent: WrappedConnectedComponent<T> = component as any
if (wrappedComponent.getWrappedInstance) {
return wrappedComponent.getWrappedInstance()
}
}
return undefined
}
Here is a simple component that we'll be accessing later.
// SomeOtherComponent.tsx
import * as React from 'react'
import { connect } from 'react-redux'
class SomeOtherComponent extends React.Component {
log = () => {
console.log('logged')
}
render() {
return <div />
}
}
const mapStateToProps = () => ({})
const mapDispatchToProps = () => ({})
const ConnectedSomeOtherComponent = connect(
mapStateToProps,
mapDispatchToProps,
null,
{ withRef: true } // Important
)(SomeOtherComponent)
export default ConnectedSomeOtherComponent
export { SomeOtherComponent }
Here is the main component that does all the work.
// SomeComponent.tsx
import * as React from 'react'
import ConnectedSomeOtherComponent, { SomeOtherComponent } from './SomeOtherComponent'
import { unwrapConnectedComponent } from './connect'
class SomeComponent extends React.Component {
someOtherComponent?: SomeOtherComponent
private setSomeOtherComponent = (someOtherComponent: SomeOtherComponent) => {
this.someOtherComponent = unwrapConnectedComponent(someOtherComponent)
}
onClick = () => {
if (this.someOtherComponent) {
this.someOtherComponent.log()
}
}
render() {
return (
<div>
<button onClick={this.onClick} />
<ConnectedSomeOtherComponent ref={this.setSomeOtherComponent} />
</div>
)
}
}
export default SomeComponent

How to use this higher order component explained in the Redux Doc?

Could anyone please help me understand how to use this Higher Order Component explained in the redux document?
I know what Higher Order Component is and I have used this patter several times but I can't figure out how to use this Higher Order Component.
http://redux.js.org/docs/recipes/UsingImmutableJS.html#use-a-higher-order-component-to-convert-your-smart-components-immutablejs-props-to-your-dumb-components-javascript-props
Here is what it says...
Use a Higher Order Component to convert your Smart Component’s Immutable.JS props to your Dumb Component’s JavaScript props
Something needs to map the Immutable.JS props in your Smart Component to the pure JavaScript props used in your Dumb Component. That something is a Higher Order Component (HOC) that simply takes the Immutable.JS props from your Smart Component, and converts them using toJS() to plain JavaScript props, which are then passed to your Dumb Component.
Here is an example of such a HOC:
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} />
}
And this is how you would use it in your Smart Component:
import { connect } from 'react-redux'
import { toJS } from './to-js'
import DumbComponent from './dumb.component'
const mapStateToProps = state => {
return {
// obj is an Immutable object in Smart Component, but it’s converted to a plain
// JavaScript object by toJS, and so passed to DumbComponent as a pure JavaScript
// object. Because it’s still an Immutable.JS object here in mapStateToProps, though,
// there is no issue with errant re-renderings.
obj: getImmutableObjectFromStateTree(state)
}
}
export default connect(mapStateToProps)(toJS(DumbComponent))
By converting Immutable.JS objects to plain JavaScript values within a HOC, we achieve Dumb Component portability, but without the performance hits of using toJS() in the Smart Component.
Here is my sample code for this!!
DumbComponent
import React from 'react';
const DumbComponent = (props) => {
const {dataList} = props;
const renderList = (list) => {
return list.map((value) => {
return <li>value</li>
})
};
return (
<ul>
{renderList(dataList)}
</ul>
)
};
export default DumbComponent;
SmartComponent
import React, { Component } from 'react';
import { connect } from 'react-redux';
import DumbComponent from './DumbComponent';
//High Order Component
import toJS from './toJS';
class SmartComponent extends Component {
constructor(props) {
super(props);
}
render() {
return (
<DumbComponent dataList={this.props.dataList} />
);
}
}
function mapStateToProps(states) {
return {
//Let's say this is immutable.
dataList: state.dataList,
};
}
//this is how I use Higher Order Component.
//export default connect(mapStateToProps)(SmartComponent);
export default connect(mapStateToProps)(toJS(DumbComponent));
My Question
export default connect(mapStateToProps)(toJS(DumbComponent));
This doesn't even export SmartComponent itself. How can the parent component of SmartComponent whose child is DumbComponent use SmartComponent if it is not even exported??
Please tell me how to use this Higher Order Component in the sample code I prepared for this post.
Update
So, this is the correct way to write?
SmartComponent
import React, { Component } from 'react';
import { connect } from 'react-redux';
import DumbComponent from '../components/DumbComponent';
class SmartComponent extends Component {
constructor(props) {
super(props);
}
render() {
return (
<DumbComponent dataList={this.props.dataList} /> //immutable
);
}
}
function mapStateToProps(states) {
return {
dataList: state.dataList //immutable
};
}
export default connect(mapStateToProps)(SmartComponent);
DumbComponent
import React from 'react';
import toJS from './higher_order_components/toJS';
const DumbComponent = (props) => {
const {dataList} = props;
const renderList = (list) => {
return list.map((value) => {
return <li>{value}</li>
})
};
return (
<ul>
{renderList(dataList)}
</ul>
)
};
export default toJS(DumbComponent);
When you do something like:
let Component = connect(mapStateToProps)(OtherComponent)
Component is "smart" because it has access to the store. What you're doing in your code is doubling up on smart components.
class OtherSmartComponent {
render { return <DumbComponent {...smartProps} /> }
}
let SmartComponent = connect(mapStateToProps)(OtherSmartComponent)
That's why in the example Smart Component there is no intermediate Smart Component, just connect, mapStateToProps and DumbComponent.
Might be worth unraveling it all:
import DumbComponent from './DumbComponent'
import toJS from './toJS'
let DumbComponentWithJSProps = toJS(DumbComponent)
let SmartComponent = connect(mapStateToProps)(DumbComponentWithJSProps)
export default SmartComponent
So really, in your code, SmartComponent isn't really smart. It's just a dumb component that renders another dumb component. The terminology makes this comment seem super harsh. 🤔
To clarify your comment:
If you want a component between your connected one and the one that's run through toJS, then do that. No need to call connect twice:
// Dumb.js
import toJS from './toJS
class Dumb { .. }
export default toJS(Dumb)
-----------------------------------
// Smart.js
import Dumb from './Dumb'
class Smart {
..methods..
render() { return <Dumb {...props} /> }
}
export default connect(mapStateToProps)(Smart)

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.

React + Redux: Separating the presentation from the data

I am building a weather app with React & Redux. I've decided to venture into uncharted waters as a noob to React & Redux. I'm splitting things up into presentational components and their respective container that will handle the data. I'm having some problems wrapping my head around this though. It might come down to how I'm trying to do it I'm just really unsure.
Right now I have SearchBar, CurrentWeather, & Forecast components and an AppContainer that I'm trying to integrate those components into. I have the SearchBar component integrated into the AppContainer so far and it is working with no problems. Here is where I am getting confused. So I have provided the needed actions and components to the container and the container has been connected so when the user does a search the api call will be made and the state will update through the reducers.
That data should be available through mapStateToProps now correct?
How can I go about using that data after the user has performed the action but have it not be used upon the initial render? If AppContainer is rendering these three components I will obviously be passing props to them so they render and function as they are expected to. I'm thinking this is where a lifecycle could be used I'm just unsure of which or how to use them. My code for the AppContainer, SearcBar, & CurrentWeather are below. CurrentWeather & Forecast are nearly identical (only providing different data from different endpoints for the api) so I did not provide it. I also didn't provide the actions or reducers because I know they work fine before I decided to attempt this refactor. Maybe I need more than one container to pull this off? Any advice or direction would be greatly appreciated, thanks all and have a good night.
** Do have a side question: on _weatherSearch I have event.preventDefault(); because the SearchBar is a form element. Do I even need to provide this? If event is not what is being passed but the term I think no. The event is being used as seen below in the form element of SearchBar:
onSubmit={event => getWeather(event.target.value)}
App Container:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchCurrentWeather, fetchForecast } from '../actions/actions';
import SearchBar from '../components/SearchBar';
import CurrentWeather from '../components/CurrentWeather';
class AppContainer extends Component {
_weatherSearch(term) {
event.preventDefault();
// Here is where we go to fetch weather data.
this.props.fetchCurrentWeather(term);
this.props.fetchForecast(term);
}
render() {
const getWeather = term => {this._weatherSearch(term);};
return (
<div className="application">
<SearchBar getWeather={getWeather}/>
<CurrentWeather />
</div>
);
}
}
const mapStateToProps = ({ current, forecast }) => {
return {
current,
forecast
}
}
export default connect(mapStateToProps,
{ fetchCurrentWeather, fetchForecast })(AppContainer);
SearchBar:
import React from 'react';
const SearchBar = ({ getWeather }) => {
return(
<form className='input-group' onSubmit={event => getWeather(event.target.value)}>
<input
className='form-control'
placeholder='Search a US City' />
<span className='input-group-btn'>
<button className='btn btn-secondary' type='submit'>Submit</button>
</span>
</form>
);
}
export default SearchBar;
CurrentWeather: *NOTE: I have not removed any of the logic or data processing from CurrentWeather yet so it has not been refactored to a presentational only component yet.
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {unitConverter} from '../conversions/conversions_2.0';
export class CurrentWeather extends Component {
_renderCurrentWeather(cityData) {
const name = cityData.name;
const {temp, pressure, humidity} = cityData.main;
const {speed, deg} = cityData.wind;
const {sunrise, sunset} = cityData.sys;
return (
<tr key={name}>
<td>{unitConverter.toFarenheit(temp)} F</td>
<td>{unitConverter.toInchesHG(pressure)}"</td>
<td>{humidity}%</td>
<td>{unitConverter.toMPH(speed)}mph {unitConverter.toCardinal(deg)}</td>
</tr>
);
}
render() {
let currentWeatherData = [];
if (this.props.current) {
currentWeatherData = this.props.current.map(this._renderCurrentWeather);
}
return (
<table className="table table-reflow">
<thead>
<tr>
<th>Temperature</th>
<th>Pressure</th>
<th>Humidity</th>
<th>Wind</th>
</tr>
</thead>
<tbody>
{currentWeatherData}
</tbody>
</table>
);
}
}
function mapStateToProps({current}) {
return {current};
}
export default connect(mapStateToProps)(CurrentWeather);
Your render function is very dynamic. You can omit anything you like:
class AppContainer extends Component {
_weatherSearch(term) {
// event.preventDefault(); We can't do this because we don't have an event here...
this.props.fetchCurrentWeather(term);
this.props.fetchForecast(term);
}
render() {
const getWeather = term => { this._weatherSearch(term); };
return (
<div className="application">
<SearchBar getWeather={getWeather}/>
{ Boolean(this.props.current) && <CurrentWeather /> }
</div>
);
}
}
const mapStateToProps = ({ current }) => ({ current });
export default connect(mapStateToProps,
{ fetchCurrentWeather, fetchForecast })(AppContainer);
This is how you deal with missing data. You just either show nothing, or a message to search first, or if it's loading,you can show a spinner or throbber.
The technique used above to hide CurrentWeather is to pass a Boolean to React if we're wanting to hide the component. React ignores true, false, null and undefined.
Note that it's a good idea to only ever pass data in mapStateToProps that you'll actually be using inside the component itself. In your code you're passing current and forecast but you don't use them.
Redux will rerender when any of the mapStateToProps, mapDispatchToProps or props data changes. By returning data you'll never use you instruct Redux to rerender when it's not necessary.
I'm a react-redux noob myself :-) and I've come across similar issues.
As far as I can tell, the container/presentational separation you've made looks good, but you can go even a step further and separate the container's fetching and mounting.
The solution I'm referring to is what people variously call "higher-order components" and "wrapper components": (the code below isn't tested - it's just for illustration purposes)
import {connect} from blah;
const AppWrap = (Wrapped) => {
class AppWrapper extends Component {
constructor(props) {
super(props);
this.state = {foo: false};
}
componentWillMount() {
this.props.actions.fooAction()
.then(() => this.setState({foo: false}));
}
render() {
return (<Wrapped {...this.props} foo={this.state.foo}/>);
}
}
function mapState(state) { blah }
function mapDispatch(dispatch) { blah }
return connect(mapState, mapDispatch)(AppWrapper);
}
export default AppWrap;
Notice the = (Wrapped) => { part at the top. That is what's doing the actual "wrapping", and the argument can be named anything so long as you refer to it in the render hook.
Now inside your AppContainer, you get a this.props.foo which acts as a flag telling you that fooAction() has completed, and you can use it to render your presentational components accordingly. Until fooAction completes, you can be sure that the foo passed into AppContainer will be false.
To put what I just said into code, your AppContainer might look something like this:
import AppWrapper from './AppWrapper';
class AppContainer extends Component {
constructor(props) {
super(props);
}
render() {
return (!this.props.foo) ? <div>bar</div> : (
<div blah>
<SearchBar blah/>
<CurrentWeather blah/>
</div>
);
}
}
export default AppWrapper(AppContainer);
The benefit of using a wrapper component like this is that
you can take more control over how and when exactly the data gets rendered
account for "loading" mechanisms and logic
avoid quirky problems like having to make dispatches within componentWillMount hooks and having to deal with the consequences.
Take a look at this blog post for more information about HoCs: https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

React redux generated stateless components perfomance

I am learning redux and react. And i decided to run a simple "stress test" with lets say 15k rows of generated component (i hope i did it right).
So i have stateless component which receive common prop for example 'year'. And i want to clone this stateless component over 9000 times and update them. For example change it prop(year) from 2016 to 2015.
I built this component in my testing project and it's working, but with slow response especially in IE 11. I am new to react+redux and maybe i did something wrong in my code.
As suggested in discord chat room i have added into my Page component:
shouldComponentUpdate(nProps, nState) {
return nProps.year != this.props.year;
}
This did help a bit. But it is still slow.
Also as related question - Is it ok to use lodash.assign() to update my state?
Also i am using typescript and it seems to have not built-in polyfill for Object.assign(); That's why i decided to try lodash.
So here is my top base component app.tsx:
import * as React from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as pageActions from '../actions/page';
import User from '../components/user/User';
import Page from '../components/page/Page';
class App extends React.Component<any, any> {
render() {
const { user, page } = this.props;
const { setYear } = this.props.pageActions;
return (
<div>
<User name={user.name} />
<Page photos={page.photos} year={page.year} setYear={setYear} />
</div>
);
};
}
function mapStateToProps (state) {
return {
user: state.user, // (1)
page: state.page // (2)
};
}
function mapDispatchToProps(dispatch) {
return {
pageActions: bindActionCreators(pageActions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
And this is my page reducer:
import {assign} from 'lodash';
const INITIAL_STATE = {
year: 2016,
photos: []
};
function pageReducer(state = INITIAL_STATE,
action = {type: '', payload: null}) {
switch (action.type) {
case 'SET_YEAR':
return assign({}, state, {year: action.payload});
default:
return state;
}
}
export default pageReducer;
And Page component:
import * as React from 'react';
import {range} from 'lodash';
let StatelessSpan: React.StatelessComponent<any> = (props) => (
<span>{props.year} </span>
);
class Page extends React.Component<any, any> {
constructor(props) {
super(props);
}
private onYearBtnClick = (e) => {
this.props.setYear(+e.target.innerText);
};
shouldComponentUpdate(nProps, nState) {
return nProps.year != this.props.year;
}
render() {
const {year, photos} = this.props;
let years = range(15000).map((value, index) => {
if(index % 4===0){
return <StatelessSpan key={index} year={year} />;
}
return <span key={index}>i am empty</span>
});
return <div>
<p>
<button onClick={this.onYearBtnClick}>2016</button>
<button onClick={this.onYearBtnClick}>2015</button>
<button onClick={this.onYearBtnClick}>2014</button>
</p>
{years}
</div>;
};
}
export default Page;
One told me that innerText is experimental and non-stable, so i've changed it to textContent. Still got delay in IE.
React/Redux may be THE best way to write apps, but it's important to understand that elegancy can sometimes come at the cost of performance issues. Luckily, it's much easier to take an elegant solution and make it performant than the other way around.
I could throw a bunch of performance optimization tips at you for React and Redux, but you might be optimizing the wrong things. You need profile your app and find out performance issues you are running into.
You might find this talk extremely helpful: https://www.youtube.com/watch?v=5sETJs2_jwo. Netflix has been able to start with very slow React and really make things go super fast without making a mess of things.
I've found this discussion here: https://twitter.com/mweststrate/status/720177443521343488
So this partially answers my question about performance and gives good vision on how this both libraries behave with my case.

Resources