I'm doing a simple redux / react todo app. I can't get the todo items to show up. I'm able to console.log the data, but can't get it to appear. What am I doing wrong?
I separated the files, here is my app.js:
import React, { Component } from 'react';
import Todos from './todos';
import TodoList from "./todo_list";
export default class App extends Component {
render() {
return (
<div>
<Todos />
<TodoList/>
</div>
);
}
}
Here is the container Todos:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { addTodo } from '../actions/index';
class Todos extends Component {
constructor(props) {
super(props);
this.state = {text: ''};
}
addTodo(e) {
e.preventDefault();
this.props.addTodo(this.state.text);
this.setState({
text: ''
});
}
updateValue(e) {
this.setState({text: e.target.value})
}
render() {
return (
<div>
<form onSubmit={(e) => this.addTodo(e)}>
<input
placeholder="Add Todo"
value={this.state.text}
onChange={(e) => {
this.updateValue(e)
}}
/>
<button type="submit">Add Todo</button>
</form>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({addTodo}, dispatch);
}
export default connect(null, mapDispatchToProps)(Todos);
Here is the TodoList:
import React, {Component} from 'react';
import {connect} from 'react-redux';
class TodoList extends Component {
render() {
return (
<ul>
{ this.props.todo.map((tod) => {
return <li key={tod.message}>{ tod.message }</li>
})}
</ul>
);
}
}
function mapStateToProps({ todo }) {
console.log({ todo });
return { todo };
}
export default connect(mapStateToProps)(TodoList);
Reducer:
import { ADD_TODO } from '../actions/types';
export default function(state=[], action) {
switch(action.type) {
case ADD_TODO:
return [ action.payload.message, ...state ]
}
return state;
}
And action
import { ADD_TODO } from './types';
const uid = () => Math.random().toString(34).slice(2);
export function addTodo(message) {
const action = {
id: uid(),
message: message
};
return {
type: ADD_TODO,
payload: action
};
}
This is what I get from the console.log({todo});
Here is my reducers/index:
import { combineReducers } from 'redux';
import TodosReducer from './reducer_addTodo';
const rootReducer = combineReducers({
todo: TodosReducer
});
export default rootReducer;
It's because there's a disconnect between your TodoList and reducer. TodoList, when mapping, expects each todo to have a message prop, but your reducer, when returning next state, only includes the message in the state array, not an object with the message property:
case ADD_TODO:
return [ action.payload.message, ...state ]
Instead, do not just put the message string in the next state's array, put in the whole object:
case ADD_TODO:
return [ action.payload, ...state ]
Now every single element in the todo array will be an object and have a message and id property. Also, try using an always unique expression for key -- it really shouldn't be the todo message, nor the id you supplied because it's using Math.random which both have a possibility of keys being the same.
Related
I am working on a simple react-redux project that gets information about movies from the OMDB api based on search term provided by the user. I am currently having trouble trying to get text typed into the searchbar to update the store value corresponding to the title of the film to search for. I'm fairly new to react and completely new to redux I've only finished one other redux project before and I set up my actions and reducers in the exact same way as last time but this time I'm running into "Uncaught TypeError: dispatch is not a function". This was not a problem I encountered in the previous project and my google searching has not been very helpful thus far.
I've searched this problem on google and only found a few results and none of them seem to be having the exact same issue as me, they involve using mapDispatchToProps which I'm not using inside of my connect function. Supposedly when you write a mapStateToProps like I have, dispatch should just be passed down as a prop to the connected component but whenever I try to access it I get the aforementioned "Uncaught TypeError: dispatch is not a function" error.
here is the index.js for my component
import { connect } from 'react-redux';
import MovieSearch from './MovieSearchContainer';
import {
updateSearchTerm,
getMovies
} from './movieSearchActions';
function mapStateToProps(state){
return {
title: state.movieSearch.title,
year: state.movieSearch.year,
plot: state.movieSearch.plot,
released: state.movieSearch.released,
runtime: state.movieSearch.runtime,
genre: state.movieSearch.genre,
plot: state.movieSearch.plot,
ratings: {
IMDB: state.movieSearch.ratings.IMDB,
Metascore: state.movieSearch.ratings.Metascore
},
posterUrl: state.movieSearch.posterUrl,
cachedMovies: state.movieSearch.cachedMovies
};
}
export default connect(mapStateToProps)(MovieSearch);
here is my action
export function updateSearchTerm(searchTerm){
return {
type: "UPDATE_SEARCH_TERM",
payload: { searchTerm }
}
}
here is my jsx component
import React from 'react';
import PropTypes from 'prop-types';
import {
updateSearchTerm,
getMovies
} from './movieSearchActions';
export default class MovieSearchContainer extends React.Component
{
constructor(props) {
super(props);
this.handleUpdateSearchTerm =
this.handleUpdateSearchTerm.bind(this);
}
handleUpdateSearchTerm(event){
const { dispatch } = this.props;
const { value } = event.target;
dispatch(updateSearchTerm(value));
}
render() {
return (
<div>
<h1 className='text-center'>Movie Finder</h1>
<input type='text' className='col-sm-11' id='searchBar'
onChange={ this.handleUpdateSearchTerm }/>
<button type='button' id='getMovies' className='col-sm-
1'>Go!</button>
</div>
)
}
}
MovieSearchContainer.propTypes = {
store: PropTypes.object
}
here is the reducer
export default function movieSearchReducer(state = defaultState,
action) {
const { type, payload } = action;
switch(type){
case 'UPDATE_SEARCH_TERM': {
return {
...state,
title: payload.title
}
}
default: {
return state;
}
}
}
I expect changes in the searchbar on the component on the page to be reflected in the redux store, but instead I just get this error
The dispatch prop is only available when you are directly interacting with the redux-store. When you define something like mapDispatchToProps() and pass it as the 2nd argument to connect(), dispatch, gets passed to mapDispatchToProps().
const mapDispatchToProps = (dispatch) => {
return{
actionCreator: (arg) => {
dispatch(actionCreator(arg))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Component)
If you dont want to define mapDispatchToProps(), you can effectively bind your action-creators by passing in an object to connect() as the 2nd argument. This implicitly binds dispatch to the action-creators:
import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { updateSearchTerm, getMovies } from "./movieSearchActions";
class MovieSearchContainer extends React.Component {
constructor(props) {
super(props);
this.handleUpdateSearchTerm = this.handleUpdateSearchTerm.bind(this);
}
handleUpdateSearchTerm(event) {
const { value } = event.target;
this.props.updateSearchTerm(value);
}
render() {
console.log(this.props.movies);
return (
<div>
<h1 className="text-center">Movie Finder</h1>
<input
type="text"
className="col-sm-11"
id="searchBar"
onChange={this.handleUpdateSearchTerm}
/>
<button
type="button"
id="getMovies"
className="col-sm-
1"
>
Go!
</button>
</div>
);
}
}
MovieSearchContainer.propTypes = {
store: PropTypes.object
};
const mapStateToProps = state => {
return {
title: state.movieSearch.title,
year: state.movieSearch.year,
plot: state.movieSearch.plot,
released: state.movieSearch.released,
runtime: state.movieSearch.runtime,
genre: state.movieSearch.genre,
plot: state.movieSearch.plot,
ratings: {
IMDB: state.movieSearch.ratings.IMDB,
Metascore: state.movieSearch.ratings.Metascore
},
posterUrl: state.movieSearch.posterUrl,
cachedMovies: state.movieSearch.cachedMovies
};
};
export default connect(
mapStateToProps,
{
updateSearchTerm,
getMovies
}
)(MovieSearchContainer);
With that, you do not need to explicitly call dispatch to use your action-creator. Simply use this.props.nameOfActionCreator()
See sandbox for example: https://codesandbox.io/s/simple-redux-7s1c0
I think you should connect your component inside your jsx file. Then you can access with this.props.yourFunctionToDispatch
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {
updateSearchTerm,
getMovies
} from './movieSearchActions';
class MovieSearchContainer extends React.Component
{
constructor(props) {
super(props);
this.handleUpdateSearchTerm =
this.handleUpdateSearchTerm.bind(this);
}
handleUpdateSearchTerm(event){
const { dispatch } = this.props;
const { value } = event.target;
dispatch(updateSearchTerm(value));
}
render() {
return (
<div>
<h1 className='text-center'>Movie Finder</h1>
<input type='text' className='col-sm-11' id='searchBar'
onChange={ this.handleUpdateSearchTerm }/>
<button type='button' id='getMovies' className='col-sm-
1'>Go!</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
title: state.movieSearch.title,
year: state.movieSearch.year,
plot: state.movieSearch.plot,
released: state.movieSearch.released,
runtime: state.movieSearch.runtime,
genre: state.movieSearch.genre,
plot: state.movieSearch.plot,
ratings: {
IMDB: state.movieSearch.ratings.IMDB,
Metascore: state.movieSearch.ratings.Metascore
},
posterUrl: state.movieSearch.posterUrl,
cachedMovies: state.movieSearch.cachedMovies
};
}
MovieSearchContainer.propTypes = {
store: PropTypes.object
}
export default connect(mapStateToProps, {yourFunctionToDispatch})(MovieSearchContainer);
I have tried a lot but i couldn't figure out what is the issue.
The props in the component is coming as empty even after adding mapStateToProps and mapDispatchToProps property.Whenever i run the below code i get following error.
projList.js:94 Uncaught TypeError: _this2.props.addNewProj is not a function
My component class is given below:
import React from 'react';
import { addProj } from '../actions';
import { connect } from 'react-redux';
import C from '../constants';
class projList extends React.Component {
constructor(props){
super(props);
this.state = {
title: ''
}
}
render(){
const {title} = this.state;
return(
<section className='proj-list-container'>
<div className='form'>
<label>project Title</label>
<input type='text' onChange={(e)=>{this.setState({title: e.target.value})}}/>
<button className='submit' onClick={()=>{this.props.addNewProj(title)}}>submit</button>
</div>}
</section>
);
}
}
const mapStateToProps = (state, props) =>
({
projLists: state.addProjToList
})
const mapDispatchToProps = dispatch =>
({
addNewProj(projObj) {
dispatch(
addProj(C.ADD_PROJ, projObj)
);
}
});
export default connect (mapStateToProps, mapDispatchToProps)(projList);
export default projList;
My actions file is
import C from './constants'
export const addProj = ({title, endDate}) => {
return ({
type:C.ADD_PROJ,
payload: {
title, endDate
}
})
}
And my store file is :
import C from '../constants';
import { combineReducers } from 'redux';
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
export const addProjToList = (state=[], action) => {
switch (action.type) {
case C.ADD_PROJ :
return [
...state,
action.payload
]
default : return state
}
}
const appReducer = combineReducers({
addProjToList
});
export default (initialState={projList: []}) => {
return applyMiddleware(thunk)(createStore)(appReducer, initialState);
}
any help would be greatly appreciated. Thanks!
I have a search bar on my TODO List:
import React, { Component } from 'react';
import { FilterTasks } from '../../redux/actions/searchbar';
import reducers from '../../redux/reducers';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
class SearchBar extends Component {
render() {
const {search, value} = this.props;
return (
<input
className="form-control"
placeholder = "Filter Tasks"
onChange={(e) => FilterTasks(e.target.value)}
value={value} />
);
}
}
function mapStateToProps({tasks}) {
return {value: tasks.value};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({FilterTasks}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(SearchBar);
Here's my action to filter:
export const SEARCH = 'SEARCH';
export function FilterTasks(value) {
return {type: SEARCH, value};
}
My search bar reducer:
import {SEARCH} from '../actions/searchbar';
const initialState = {}
export default function SEARCHREDUCER(state = initialState, action) {
switch(action.type) {
case SEARCH: {
const {value} = action;
const tasks = state.contents.filter((val) => val.includes(value));
return {...state, value, tasks};
}
default:
return state;
}
}
My Index reducer:
import { combineReducers } from 'redux';
import SEARCHREDUCER from '../reducers/searchbar';
const TaskReducer = (state = [] ,action) => {
switch (action.type){
case 'ADD_TASK':
state = state.concat(action.payload);
break;
case 'DELETE_TASK':
state = state.tasks.filter(task => task !== action.payload)
break;
}
return state;
},
reducers = combineReducers({
tasks : TaskReducer,
SEARCHREDUCER
});
export default reducers;
And my TasksList class where the filtered list should be rendered:
import React, { Component } from 'react';
import {connect} from 'react-redux';
import Task from '../task'
class TaskList extends Component {
render(){
return(
<table>
<thead>
<tr>
<th>Tasks</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{this.props.tasks.map((task,index) => <Task key={index} task={task} />)}
</tbody>
</table>
);
}
}
function MapStateToProps(state){
return{
tasks:state.tasks,
}
}
export default connect (MapStateToProps)(TaskList);
My problem here is that when I type an entry on the search bar the Tasks list does not change at all, It's not showing any kind of error. What i'm missing here?
FilterTasks(e.target.value) should be this.props.FilterTasks(e.target.value) instead, otherwise it'll call the the imported function from actions that is not bound to Redux by your mapDispatchToProps.
Also, your TaskReducer and SEARCHREDUCER are wrong. The reducer variable is the one with the combined state, not TaskReducer or SEARCHREDUCER.
You should just keep the search string in state and do the filtering within TaskList with this.props.tasks.filter(<insert filter function>).map(<insert map function>).
I've set up my Redux to capture a user selection from a webshop (item, size, price) and send it to another Cart component. This is working perfectly, but I want to capture an image of the item and send it to Cart. Within each product page where you can add an item to the cart there is an image that I also would like to send with the user selection. This is an example of the product page component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { addCart } from '../../actions';
import SeltzShirt from './seltzshirt.jpg';
import Slideone from './slideSeltzOne';
import Slidetwo from './slideSeltzTwo';
import RightArrow from './rightarrow';
import LeftArrow from './leftarrow';
export class ProductPage3 extends Component {
constructor(props) {
super(props);
this.state = {
slideCount: 1,
value: 'medium', cartData: {}
}
this.nextSlide = this.nextSlide.bind(this);
this.previousSlide = this.previousSlide.bind(this);
this.handleClick = this.handleClick.bind(this);
this.change = this.change.bind(this);
}
handleClick() {
let cart = {price:25,item:this.description.innerHTML,size:this.state.value};
this.props.onCartAdd(cart);
console.log(cart);
this.itemSelection(cart);
}
...
componentDidMount () {
window.scrollTo(0, 0)
}
render() {
return (
<div className= "ProductPage" id="ProductPage">
<div id='slider'>
{this.state.slideCount === 1 ? <Slideone /> : null}
{this.state.slideCount === 2 ? <Slidetwo /> : null}
<RightArrow nextSlide={this.nextSlide} />
<LeftArrow previousSlide={this.previousSlide} />
</div>
<div id='InfoSquare'>
<div id='wrapper'>
<div id='item' ref={i=>this.description=i}>LOGO TEE</div>
<div id='description'>Black tee 100% cotton with red silkscreened logo on front and back.</div>
<select id="size2" onChange={this.change} value={this.state.value}>
<option value="medium">Medium</option>
<option value="large">Large</option>
<option value="x-large">X-large</option>
</select>
<button onClick={this.handleClick} className="addit">ADD TO CART</button>
</div>
</div>
</div>
);
}
nextSlide() {
this.setState({ slideCount: this.state.slideCount + 1 })
}
previousSlide() {
this.setState({ slideCount: this.state.slideCount - 1 })
}
}
const mapDispatchToProps = (dispatch) => {
return {
onCartAdd: (cart) => {
dispatch(addCart(cart));
},
}
}
function mapStateToProps(state) {
return {
cart: state.cart
};
}
export default connect(mapStateToProps,mapDispatchToProps)(ProductPage3);
This is my Cart component:
import React, { Component } from 'react';
import {addCart} from './Shop';
import { removeCart } from '../../actions';
import { connect } from 'react-redux';
export class Cart extends Component {
constructor(props) {
super(props);
this.state = {items: this.props.cart,cart: [],total: 0};
}
...
render() {
return(
<div className= "Webcart" id="Webcart">
<div id='WebcartWrapper'>
<ul id='webCartList'>
{this.state.items.map((item, index) => {
return <li className='cartItems' key={'cartItems_'+index}>
<h4>{item.item}</h4>
<p>Size: {item.size}</p>
<p>Price: {item.price}</p>
<button onClick={() => this.handleClick(item)}>Remove</button>
</li>
})}
</ul>
<div>Total: ${this.countTotal()}</div>
</div>
</div>
);
}
}
const mapDispatchToProps = (dispatch) => {
return {
onCartAdd: (cart) => {
dispatch(addCart(cart));
},
onCartRemove: (item) => {
dispatch(removeCart(item));
},
}
}
function mapStateToProps(state) {
return { cart: state.cart };
}
export default connect(mapStateToProps, mapDispatchToProps)(Cart);
In Cart I'm rendering the item selection data for each object added to the cart. Here is where I want to display the item image also.
Since I have a image slider set up, an example of one of the slides would be:
import React, { Component } from 'react';
import take1 from './DETAIL.png';
const SlideNocHOne= (props) => {
return <img src= {take1} id="slide"></img>
}
export default SlideNocHOne;
Let's say I want this DETAIL.png image on the Cart, how could I transfer it with the user selection using Redux?
These are my Redux components:
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import reducer from './reducers';
import thunkMiddleware from 'redux-thunk';
import {createLogger} from 'redux-logger';
const store = createStore(
reducer,
undefined,
compose(
applyMiddleware(createLogger(), thunkMiddleware),
autoRehydrate()
)
);
persistStore(store, {whitelist: ['cart']});
export default store;
import {ADD_CART} from './actions';
import {REMOVE_CART} from './actions';
import { REHYDRATE } from 'redux-persist/constants';
export default Reducer;
var initialState = {
cart:{},
data: [],
url: "/api/comments",
pollInterval: 2000
};
function Reducer(state = initialState, action){
switch(action.type){
case REHYDRATE:
if (action.payload && action.payload.cart) {
return { ...state, ...action.payload.cart };
}
return state;
case ADD_CART:
return {
...state,
cart: [...state.cart, action.payload]
}
case REMOVE_CART:
return {
...state,
cart: state.cart.filter((item) => action.payload !== item)
}
default:
return state;
};
}
export const ADD_CART = 'ADD_CART';
export const REMOVE_CART = 'REMOVE_CART';
export function addCart(item){
return {
type: ADD_CART,
payload: item
}
};
export function removeCart(item){
return{
type:REMOVE_CART,
payload: item
}
};
How can I use my Redux setup to transfer the image of a user selection to Cart?
If the path's of your components are relatively stable and you have a single location for the images, you can simply have a function that takes a component's displayName (in your example, Cart, etc.) and returns the relative path the image dir.
If you have that, you can just save a key/value collection in the reducer for what images each component should have, like:
{
CartComponent: ['DETAIL.png', 'DETAIL_2.png']
...
}
When rending just use the mapper function which will provide you a relative path and that's it. Something like (or you can just map out that array):
const relativeImagePath = getRelativeImageDirPathByCompName('CartComponent') + this.props.images.CartComponent[0];
Use require to fetch the image in the template like:
<img src={require(relativeImagePath)} alt="Something"/>
I have this autocomplete component that takes an array of terms as a dataSource prop. The data I want to feed in resides in a public API, and I've followed the tutorial here to get to the code below. But this tutorial (and many others out there) explain how to bind these actions to an event, whereas I want to populate this prop with data on page load. How would I go about doing that?
actions.js
import fetch from 'isomorphic-fetch';
export function loadSchools(termId) {
return {
type: 'LOAD_SCHOOLS',
termId
};
}
export function receiveSchools(termId, json) {
return {
type: 'RECEIVE_SCHOOLS',
termId,
schools: json.data.children.map(child => child.data), // ???
receivedAt: Date.now()
};
}
export function getSchools(termId) {
return function (dispatch) {
dispatch(loadSchools(termId));
return fetch('http://www.northwestern.edu/class-descriptions/4650/index-v2.json')
.then(response => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => dispatch(receiveSchools(termId, data)));
};
}
reducers.js
const initialState = {
schoolsData: {
isFetching: false,
lastUpdated: 0,
schools: []
}
};
function schools(state = initialState, action) {
switch (action.type) {
case 'LOAD_SCHOOLS':
return {
...state,
isFetching: true
};
case 'RECEIVE_SCHOOLS':
return {
...state,
isFetching: false,
schools: action.schools,
lastUpdated: receivedAt
}
default:
return state;
}
}
export default schools;
Search.jsx
import React from 'react';
import AutoComplete from 'material-ui/AutoComplete';
export default class Search extends React.Component {
render() {
return (
<AutoComplete
hintText="Search for something."
dataSource={this.props.searchdata}
maxSearchResults={15}
filter={AutoComplete.caseInsensitiveFilter}
onNewRequest={}
/>
);
}
}
Search.propTypes = {
searchdata: React.PropTypes.array.isRequired,
onSelect: React.PropTypes.func
};
index.jsx
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { grey500, white, fullBlack } from 'material-ui/styles/colors';
import { fade } from 'material-ui/utils/colorManipulator';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import schools from './reducers/reducers';
import colors from './colors';
import NavBar from './components/NavBar.jsx';
import Serif from './components/Serif.jsx';
const store = createStore(schools, applyMiddleware(thunkMiddleware));
const muiTheme = getMuiTheme({
palette: {
primary1Color: colors.northwesternPurple,
primary2Color: colors.northwesternPurple120,
primary3Color: grey500,
accent1Color: colors.northwesternPurple30,
accent2Color: colors.richBlack10,
accent3Color: colors.richBlack50,
textColor: colors.richBlack80,
alternateTextColor: white,
canvasColor: white,
borderColor: colors.richBlack20,
disabledColor: fade(colors.richBlack80, 0.3),
pickerHeaderColor: colors.northwesternPurple,
clockCircleColor: fade(colors.richBlack80, 0.07),
shadowColor: fullBlack
}
});
class App extends React.Component {
render() {
return (
<Provider store={store}>
<MuiThemeProvider muiTheme={muiTheme}>
<div> {/* MuiThemeProvider requires stricly one child element */}
<NavBar />
<Serif /> {/* This component contains SearchContainer, which in turn contains Search */}
</div>
</MuiThemeProvider>
</Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
You can render your Search component from another component, let's call it SearchContainer. SearchContainer is decorated by the connect function from react-redux that has as only role to dispatch the action to fetch the schools. SearchContainer doesn't render Search component until the school are fetched.
Here an example of what the code would look like. Here I assume you don't use react-redux.
First you have a small problem in your initial state in reducers.js. It should be:
const initialState = {
isFetching: false,
lastUpdated: 0,
schools: []
};
function schools(state = initialState, action) {
switch (action.type) {
case 'LOAD_SCHOOLS':
return {
...state,
isFetching: true
};
case 'RECEIVE_SCHOOLS':
return {
...state,
isFetching: false,
schools: action.schools,
lastUpdated: receivedAt
}
default:
return state;
}
}
SearchContainer.js
// ./containers/SearchContainer.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadSchools } from '../actions/actions'
import Search from '../components/Search';
class SearchContainer extends Component {
componentDidMount() {
this.props.loadSchools(this.props.termId);
},
render() {
const {
schools,
isFetching
} = this.props;
if (isFetching) {
return null;
}
return <Search schools={schools} />;
}
}
const mapStateToProps = (state) => ({
isFetching: state.isFetching,
schools: state.schools
});
const mapActionsToProps = (dispatch) => ({
loadSchools: (termId) => dispatch(loadSchools(termId)),
});
export default connect(mapStateToProps, mapActionsToProps)(SearchContainer);
In this way, at the first render, your Search component is not rendered. It is rendered, only after the schools are loaded.
You can dispatch the LOAD_SCHOOLS action from the componentDidMount lifecycle method (maybe in your Serif component but I can't see the code for that).
From the docs:
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
https://facebook.github.io/react/docs/react-component.html#componentdidmount