Handling updates to store with React/Redux and lifecycle events - reactjs

I'm using React with Redux on my front end and using the Rails API to handle my backend. At present, I am trying to update a list of articles based on user addition of an article. The ArticleForm component fires an action creator that is successfully updating my ArticleList. However, at present the life cycle method componentWillUpdate is firing continuously making axios requests to Rails, and Rails keeps querying my database and sending back the articleList.
Note: I have tried using shouldComponentUpdate as such to no avail, the DOM doesn't update:
// shouldComponentUpdate(newProps){
// return newProps.articleList !== this.props.articleList
// }
My question is: how can I use React's lifecycle methods to avoid this from happening and only happening when my articleList updates. Am I going down the wrong path using lifecycle methods? I'm fairly new to React/Redux so any and all advice is helpful!
I have the following container:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import ArticleForm from './ArticleForm'
import ArticleList from './ArticleList'
import removeArticle from '../actions/removeArticle'
import fetchArticles from '../actions/fetchArticles'
import updateArticleList from '../actions/updateArticleList'
class DumbArticleContainer extends Component {
componentWillMount() {
this.props.fetchArticles()
}
// shouldComponentUpdate(newProps){
// return newProps.articleList !== this.props.articleList
// }
componentWillUpdate(newProps){
if (newProps.articleList.articleList.count !== this.props.articleList.articleList.count){
this.props.updateArticleList()
}
}
render() {
return(
<div>
<ArticleForm />
<ArticleList articleList={this.props.articleList} />
</div>
)
}
}
const ArticleContainer = connect(mapStateToProps, mapDispatchToProps)(DumbArticleContainer)
function mapStateToProps(state) {
return {articleList: state.articleList}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({removeArticle, fetchArticles, updateArticleList}, dispatch);
}
export default ArticleContainer
here is the ArticleForm
import React, { Component, PropTypes } from 'react'
import { reduxForm } from 'redux-form'
import addArticle from '../actions/addArticle.js'
class ArticleForm extends Component {
constructor(props) {
super(props)
this.state = {disabled: true}
}
/* Most article elements are displayed conditionally based on local state */
toggleState(){
this.setState({
disabled: !this.state.disabled
})
}
handleFormSubmit(props) {
event.preventDefault()
const {resetForm} = this.props
this.props.addArticle(props).then( ()=>{
var router = require('react-router')
router.browserHistory.push('/dashboard')
resetForm()
})
}
render() {
const disabled = this.state.disabled ? 'disabled' : ''
const hidden = this.state.disabled ? 'hidden' : ''
const {fields: {title, url}, handleSubmit} = this.props;
return (
<div className="article-form">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<button className="article-form-btn"
hidden={!hidden}
onClick={this.toggleState.bind(this)}
>
+ Add Article
</ button>
<input className="article-form-input"
hidden={hidden}
type="textarea"
placeholder="Title"
{...title}
/>
<input className="article-form-input"
hidden={hidden}
type="textarea"
placeholder="Paste Link"
{...url}
/>
{ this.state.disabled
? ''
: <input className="article-form-input"
type="submit"
value="Save"
/>
}
</form>
</div>
);
}
}
export default reduxForm({
form: 'articleForm',
fields: ['title', 'url']
},
null,
{ addArticle })(ArticleForm);
and the ArticleList
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import removeArticle from '../actions/removeArticle.js'
import fetchArticles from '../actions/fetchArticles'
import { ListGroup } from 'react-bootstrap'
import { ListGroupItem } from 'react-bootstrap'
class Article extends Component {
render(){
var articleList = this.props.articleList.articleList
return(
<div>
<ListGroup>
{ articleList.slice(articleList.length - 10, articleList.length)
.map( (article) => {
return(
<ListGroupItem href="#" header={article.attributes.title}>
{article.attributes.url}
</ListGroupItem>
)}
)}
</ListGroup>
<div> View All Articles </div>
</div>
)
}
}
const ArticleList = connect(mapStateToProps, mapDispatchToProps)(Article)
function mapStateToProps(state) {
return {articleList: state.articleList}
}
function mapDispatchToProps(dispatch) {
return {removeArticle: bindActionCreators({removeArticle}, dispatch),
fetchArticles: bindActionCreators({fetchArticles}, dispatch)
}
}
export default ArticleList
action creator:
So here is my action creator import axios from 'axios'
import axios from 'axios'
function updateArticleList(){
const url = 'http://localhost:3000/api/v1/articles'
return axios.get(url).then( (response)=> {
return {
type: 'UPDATE_ARTICLE_LIST',
payload: response.data
}
})
}
export default updateArticleList
and reducer:
export default function articleReducer(state = {articleList: []}, action) {
switch(action.type){
case 'FETCH_ARTICLES':
return Object.assign({}, state, {articleList: action.payload.data});
case 'UPDATE_ARTICLE_LIST':
return Object.assign({}, state, {articleList: action.payload.data});
default:
return state
}
}
There is no issue with the store nor the action creators nor the reducers, they are all working pretty well. I can't really replicate the hundreds of queries rails is performing but am happy to include other code should anyone need to see it.
Thanks!

Your mapDispatchToProps is using bindActionCreators wrong. Instead of
function mapDispatchToProps(dispatch) {
return {removeArticle: bindActionCreators({removeArticle}, dispatch),
fetchArticles: bindActionCreators({fetchArticles}, dispatch)
}
}
you should use
function mapDispatchToProps(dispatch) {
return bindActionCreators({removeArticle, fetchArticles}, dispatch);
}
bindActionCreators can, as the name suggests, bind more than one action creator.
This probably won't solve your issue but an answer is the only place I could put this nicely.
Note that you'll need to fix how you're using it as well. No more double names.

I'd like to keep a state called shouldUpdateList. Whenever I fire a action that changes the list(add or update an item to the list), I set shouldUpdateList to true. Then,set it back to false whenever I fire ajax action to fetch the list.
The lifecycle event I use to check shouldUpdateList is componentWillReceiveProps, if it's true I fire a fetch action.
EDIT: I mean keep shouldUpdateList state in Redux store. Something like:
const INIT_STATE = {
list: [],
shouldUpdateList: false
}
then
case Action.ADD_NEW:
//set shouldUpdateList to true
case Action.FETCH_LIST:
//set shouldUpdateList to false
lastly, in component
componentWillReceiveProps(nextProps) {
if(nextProps.shouldUpdateList === true) {
//dispatch action FETCH_LIST
}
}

Related

react doesn't get re-render after getting new store state into props

I'm using react and react-redux.
I used mapstatetoprops and mapdispatchtoprops to update view of my react component.
Except re-render doesn't work after redux store changed, everything works fine. Action dispatch works fine, reducer works fine, I can console.log store state and check difference.
At first, I used useDispatch and useSelector and everything worked fine. But I'm changing it to mapdispatchtoprops and mapstatetoprops to merge my code into my project teammate's code.
I tried to put this.props.(whatineed) directly in my render()'s return in component. As I understand, through mapstatetoprops, store state should be passed into my component's props.
import React, { Component } from 'react';
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap';
import { useSelector, useDispatch } from 'react-redux';
import { checked, notchecked } from '../../../actions';
import { connect } from "react-redux";
import local from './address';
import './index.css';
const mapStateToProps = state => {
return {
localsel : state.selectedLocal.locals
}
}
let mapDispatchToProps = (dispatch) => {
return {
check: (btn) => dispatch(checked(btn)),
uncheck: (btn) => dispatch(notchecked(btn))
}
}
class Seoul extends Component {
constructor(props){
super(props)
}
render(){
var btnclicked = (e) => {
let btnname = e.target.parentNode.getAttribute('id');
if (e.target.checked) {
console.log('checked');
this.props.check(btnname);
};
if (!e.target.checked) {
console.log('not checked');
this.props.uncheck(btnname);
};
// HERE IS WHERE I CAN CHECK THE PASSED STORE STATE
console.log(this.props.localsel);
// -------------------------------------------------
}
return (
<div className='localdiv localdiv1'>
// HERE IS WHERE I WANT TO SEE MY STORE STATE
{this.props.localsel.map(val=>{
return <h1>{val}</h1>
})}
// --------------------------------------------
<ToggleButtonGroup className='togglebtngrp' type="checkbox">
<ToggleButton className='togglebtn0' onChange={btnclicked} variant="outline-secondary" value={0} id="entireseoul">Entire Seoul</ToggleButton>
{local.Seoul.map((value, index) => {
return (<ToggleButton key={index} className='togglebtn' onChange={btnclicked} variant="outline-primary" value={index + 1} id={value}>{value}</ToggleButton>)
})}
</ToggleButtonGroup>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Seoul);
this component is exported in parent component, which is
import React, { Component } from 'react';
import { Jumbotron } from 'react-bootstrap';
import { Gyeongi, Incheon, Busan, Daegue, Daejeon, Sejong, Gwangju, Ulsan, Gangwon, Gyungnam, Gyungbuk, Jeonnam, Jeonbuk, Choongnam, Choongbuk, Jeju, Othercountry } from './Locals';
import Seoul from './Locals';
import './Detailsrch.css';
class Detailsrch extends Component{
render(){
var localselect = (e) => {
let selector = document.getElementsByClassName('locals');
let selector_local = document.getElementsByClassName('localdiv');
let i = 0;
for (let j = 0; j < selector_local.length; j++) {
selector_local[j].style.display = 'none';
}
let boxclass = e.target.getAttribute('name');
if (boxclass) document.getElementsByClassName(boxclass)[0].style.display = 'block';
while (selector[i]) {
selector[i].className = 'locals';
i++;
}
if (e.target.className == 'localtext') {
e.target.parentElement.className = 'locals localclick';
} else {
e.target.className = 'locals localclick';
}
}
return (
<Jumbotron className='searchjumbo'>
<p>Locals</p>
<Seoul />
<Gyeongi />
<Incheon />
<Busan />
<Daegue />
<Daejeon />
<Sejong />
<Gwangju />
<Ulsan />
<Gangwon />
<Gyungnam />
<Gyungbuk />
<Jeonnam />
<Jeonbuk />
<Choongnam />
<Choongbuk />
<Jeju />
<Othercountry />
<hr className='firsthr' />
<p>type</p><hr />
<p>career</p><hr />
<p>country</p><hr />
<p>sex</p>
</Jumbotron>
);
}
};
export default Detailsrch;
here's my reducer
import { combineReducers } from 'redux';
const initialstate = {
locals: []
}
const localSelector = (state = initialstate, action) => {
switch(action.type){
case 'CHECKED':
if(action.payload){
var arr = state.locals;
arr.push(action.payload);
return {
...state,
locals: arr
};
} else {
return state;
}
case 'NOTCHECKED':
if(action.payload){
var arrnum = state.locals.indexOf(action.payload);
var arr = state.locals;
arr.splice(arrnum, 1);
return {
...state,
locals: arr
};
} else {
return state;
}
default:
return state;
}
};
const rootReducer = combineReducers({
selectedLocal: localSelector
});
export default rootReducer;
I expect when props value changes, component will re-render and I will see the change in the browser. Props value has changed, but nothing happens in browser.
You are mutating the redux state as below
var arr = state.locals;
arr.push(action.payload);
The redux state should be immutable. You can have a look at here for some tips on how to update the redux store in reducer.
https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns
I can't see Detailsrch imported in Seoul but it's vice versa Seoul is imported in Detailsrch and as per the code and comments i can see this.props.localsel is changing and this is used in Seoul so render method of Seoul will be called and since there is no mapping or usage of this.props.localsel in Detailsrch so render method will not be called.
So if you want to re-render Detailsrch you need to change the mapping of this.props.localsel from Seoul to Detailsrch and pass this value as props to Seoul it should be working.
If still issue exists please post your code to sandbox/codepen/jsfiddle so that we can reproduce.
Probably you are suffering from something like this post mentioned. One trick to make your component to get the changes from the store (When passing state value from the store as a prop) is to make a deep clone of your props (Or the prop in which you want to get the change), for this you could use JSON:
JSON.parse(JSON.stringify(propToClone));
Hope this helps.
P.S.: Don't clone props that are functions, because JSON will erase/ignore them.

Redux-Form Initial values

So I'm trying to load a Redux Form pre populated with values from my store. However I'm unable to get anything back other than null. Been at this for several hours now and been reading over several SO examples trying different things and think I'm just at a wall on this.
Following this Redux Form Initializing from State example
Redux: 3.6.0
React-Redux: 5.0.3
Redux-Form: 6.6.3
React: 15.4.2
There is a parent component that is rendering a child component which is the form. For sake of brevity going to put in the bare minimum of code and make names as generic as possible. Everything loads fine but I think the issue relies in not properly connecting to my store. Or rather I should just be loading the data on a componentWillMount?
Parent Component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUser } from '../actions/usersActions';
import ChildForm from './ChildForm.jsx'
#connect((store) => {
return{
user: store.users.user
}
})
export default class Parent extends Component{
componentWillMount(){
this.props.dispatch(fetchUser(this.props.match.params.id))
}
submit = (values) => {//do some stuff}
render(){
return(
<ChildForm onSubmit={this.submit} />
);
}
}
ChildForm:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { user } from './reducers/usersReducer.js';
class ChildForm extends Component{
render(){
console.log('FORM STATE >>>>>>>>>>', this.state); //Returns NULL
const { handleSubmit } = this.props;
return(
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="firstName">First Name</label>
<Field name="first_name" component="input" type="text"/>
</div>
<button type="submit">Submit</button>
</form>
);
}
}
ChildForm = reduxForm({
form: 'childForm',
enableReinitialize : true // I found this in another SO Example
})(ChildForm);
ChildForm = connect(
state => ({
user: state.user,
initialValues: state.user
}),
{ fetchUser }
)(ChildForm)
export default ChildForm;
enableReinitialize SO
usersReducer.js
export default function reducer(state={
user: {},
}, action){
switch (action.type){
case "FETCH_USER_FULFILLED":{
return{
...state,
user: action.payload
}
}
}
return state;
}
So this is where I'm at currently. Can get the page, form, and submit all work. However I can't seem to figure out how to get my Store values out and into the form fields. Any help would be greatly appreciated.
Looks like everything is wired up correctly but I wasn't pulling in the correct object in the store.
ChildForm = connect(
state => ({
initialValues: state.users.user
}),
{ fetchUser }
)(ChildForm)
...Always something little

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

Decoupling React Components and Redux Connect

As seen here I am trying to decouple my app's components as much as I can and make them not aware of any storage or action creator.
The goal is to have them to manage their own state and call functions to emit a change. I have been told that you do this using props.
Considering
// Menu.jsx
import React from 'react'
import { className } from './menu.scss'
import Search from 'components/search'
class Menu extends React.Component {
render () {
return (
<div className={className}>
<a href='#/'>Home</a>
<a href='#/foo'>foo</a>
<a href='#/bar'>bar</a>
<Search />
</div>
)
}
}
And
// Search.jsx
import React from 'react'
import { className } from './search.scss'
class Search extends React.Component {
render () {
let { searchTerm, onSearch } = this.props
return (
<div className={`search ${className}`}>
<p>{searchTerm}</p>
<input
type='search'
onChange={(e) => onSearch(e.target.value)}
value={searchTerm}
/>
</div>
)
}
}
Search.propTypes = {
searchTerm: React.PropTypes.string,
onSearch: React.PropTypes.function
}
export default Search
And reading here I see a smart use of Provider and connect and my implementation would look something like this:
import { bindActionCreators, connect } from 'redux'
import actions from 'actions'
function mapStateToProps (state) {
return {
searchTerm: state.searchTerm
}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
dispatchSearchAction: actions.search
}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Search)
Assuming I have a store handling searchTerm as part of the global state.
Problem is, where does this code belongs to? If I put it in Search.jsx I will couple actions with the component and more important to redux.
Am I supposed to have two different versions of my component, one decoupled and one connect()ed and have <Menu /> to use it? If yes what would my files tree look like? One file per component or a like a make-all-connected.js ?
In redux, exist a new kind of component that is called containers, this is the component that use connect(mapStateToProps, mapActionsToProps), to pass the state and actions to the current component.
All depends of the use of the component. For example, if you component Search only going to be use with the same state and action, You container could be the same that your component like this:
// Search.jsx
import { connect } from 'redux'
import actions from 'actions'
import React from 'react'
import { className } from './search.scss'
class Search extends React.Component {
render () {
let { searchTerm, onSearch } = this.props
return (
<div className={`search ${className}`}>
<p>{searchTerm}</p>
<input
type='search'
onChange={(e) => onSearch(e.target.value)}
value={searchTerm}
/>
</div>
)
}
}
Search.propTypes = {
searchTerm: React.PropTypes.string,
onSearch: React.PropTypes.function
}
function mapStateToProps ({searchTerm}) {
return {
searchTerm
};
}
const mapDispatchToProps = {
onSearch: actions.search
}
export default connect(mapStateToProps, mapDispatchToProps)(Search)
But if your plan is reuse this component in another containers and the searchTerm or the action are different on the global state. The best way is passing this properties through other containers, and keep the Search component pure. Like this:
// Container1.jsx
import { connect } from 'redux'
import actions from 'actions'
import React, { Component } from 'react'
class Container1 extends Component {
render() {
const { searchTerm, handleOnSearch } = this.props;
return (
<div>
<Search searchTerm={searchTerm} onSearch={handleOnSearch} />
</div>
)
}
}
function mapStateToProps ({interState: {searchTerm}}) {
return {
searchTerm
};
}
const mapDispatchToProps = {
handleOnSearch: actions.search
}
export default connect(mapStateToProps, mapDispatchToProps)(Container1)
// Container2.jsx
import { connect } from 'redux'
import otherActions from 'otheractions'
import React, { Component } from 'react'
class Container2 extends Component {
render() {
const { searchTerm, handleOnSearch } = this.props;
return (
<div>
<Search searchTerm={searchTerm} onSearch={handleOnSearch} />
</div>
)
}
}
function mapStateToProps ({otherState: {searchTerm}}) {
return {
searchTerm
};
}
const mapDispatchToProps = {
handleOnSearch: otherActions.search
}
export default connect(mapStateToProps, mapDispatchToProps)(Container2)
For more information, read the official docs about using redux with react.

Resources