How to dynamically update a search term with React/Redux/Axios? - reactjs

I'm building a small itunes application to fetch data from the Itunes api. I'm trying to implement the searchbar to make a relevant fetch to the server but I can't seem to figure out the correct props passed down from the Redux state. Since it is a small app I have used just the main App.js and index.js instead of separate folders for actions and reducers. My main App.js is as below:
import React, { Component } from 'react';
import './App.css';
import Navbar from './components/Navbar';
import Results from './components/Results';
import ToggleLayout from './components/ToggleLayout';
import AdditionalPages from './components/AdditionalPages';
import axios from 'axios';
import { connect } from 'react-redux';
const PATH_BASE = 'https://itunes.apple.com/search';
const PATH_TERM = 'term=';
const COUNTRY = 'country=es';
const ALBUMS = 'entity=album';
const LIMIT = 'limit=60';
class App extends Component {
constructor(props){
super(props);
}
}
render() {
return (
<div>
<Navbar
searchTerm={this.props.searchItunes.searchTerm}
onSearchChange={(e) => this.props.onSearchChange(e.target.value)}
fetchITunesAlbums={(e) => this.props.fetchITunesAlbums(e)}
/>
{ this.props.searchItunes.itunes &&
<Results itunes={this.props.searchItunes.itunes} grid={this.props.toggle.grid} additionalPages={this.props.toggle.additionalPages} fetchMorePages={this.fetchMorePages}/>
}
{ this.props.toggle.additionalPages &&
<AdditionalPages itunes={this.props.searchItunes.itunes} grid={this.props.toggle.grid}/>
}
<ToggleLayout
switchLayout={()=> this.props.switchLayout()}
grid={this.props.toggle.grid}
/>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
toggle: state.booleanReducer,
searchItunes: state.searchItunesReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
switchLayout: () => {
dispatch({
type:"GRID"
});
},
fetchMorePages: () => {
dispatch({
type:"ADDITIONALPAGES"
});
},
onSearchChange: (term) => {
dispatch({
type:"SEARCHTERM",
payload:term
});
},
fetchITunesAlbums: (e) => {
e.preventDefault();
axios.get(`${PATH_BASE}?${PATH_TERM}${searchTerm}&${COUNTRY}&${ALBUMS}&${LIMIT}`)
.then(response =>{
dispatch({
type: 'FETCHITUNESALBUMS',
payload: response.data
});
});
}
};
};
export default connect(mapStateToProps,mapDispatchToProps)(App);
So my issue is with my axios url. For example if I hard code the url such as
axios.get(`${PATH_BASE}?${PATH_TERM}&${'someband'}${COUNTRY}&${ALBUMS}&${LIMIT}`)
the I'm able to fetch the results from the server but not when I insert
axios.get(`${PATH_BASE}?${PATH_TERM}${searchTerm}&${COUNTRY}&${ALBUMS}&${LIMIT}`)
and below is my index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
const booleanReducer = (state = { grid:true, additionalPages:false }, action) => {
if (action.type === 'GRID'){
return state = {
...state,
grid:!state.grid
}
}
if (action.type === 'ADDITIONALPAGES'){
return state = {
...state,
additionalPages:!state.additionalPages
}
}
return state;
};
const searchItunesReducer = (state = { searchTerm:'', itunes:null }, action) => {
if (action.type === 'SEARCHTERM'){
return state = {
...state,
searchTerm:action.payload
}
}
if (action.type === 'FETCHITUNESALBUMS'){
return state = {
...state,
itunes: action.payload
}
}
return state;
}
const middleware = applyMiddleware(thunk, logger)
const store = createStore(combineReducers({booleanReducer,searchItunesReducer}),middleware);
console.log(store.getState());
store.subscribe(() =>{
console.log("store updated!", store.getState());
});
registerServiceWorker();
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
Any help is highly appreciated...

I think your issue lies with the search term, try to pass the value as a param,
<Navbar
searchTerm={this.props.searchItunes.searchTerm}
onSearchChange={(e) => this.props.onSearchChange(e.target.value)}
fetchITunesAlbums={(e) => this.props.fetchITunesAlbums(e,this.props.searchItunes.searchTerm)}
/>
and then the fetchItunes function as,
fetchITunesAlbums: (e,searchTerm) => {
e.preventDefault();
axios.get(`${PATH_BASE}?${PATH_TERM}${searchTerm}&${COUNTRY}&${ALBUMS}&${LIMIT}`)
.then(response =>{
dispatch({
type: 'FETCHITUNESALBUMS',
payload: response.data
});
});
}
};
};

Can you try this on the search bar
onChange={(e) => props.fetchItunesAlbums(e.target.value)}
And update your fetchItunesAlbums to:
axios.get(`${PATH_BASE}?${PATH_TERM}${e}&${COUNTRY}&${ALBUMS}&${LIMIT}`)
Instead of saving the search term on app state redux. see if it works.

Related

dispatching action in redux-saga is not fetching data

I am studying redux-saga and I want to fetch data from :
https://jsonplaceholder.typicode.com/posts
and in my redux folder I have the fallowing:
(it can be checked in this github repository
https://github.com/jotasenator/redux-saga-fetching-example/tree/main/src)
\src\redux\api.js
import axios from 'axios'
export const loadPostApi = async () => {
await axios.get(`https://jsonplaceholder.typicode.com/posts`)
}
the get request to the address in question
src\redux\app.actions.js
export const loadPostStart = () => ({
type: 'LOAD_POST_START',
})
export const loadPostSuccess = (posts) => ({
type: 'LOAD_POST_SUCCESS',
payload: posts,
})
export const loadPostFail = (error) => ({
type: 'LOAD_POST_FAIL',
payload: error,
})
those are the actions functions
src\redux\app.reducer.js
const INITIAL_STATE = {
loading: false,
posts: [],
errors: null,
}
export const appReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'LOAD_POST_START':
return {
...state,
loading: true,
}
case 'LOAD_POST_SUCCESS':
return {
...state,
posts: action.payload,
loading: false,
}
case 'LOAD_POST_FAIL':
return {
...state,
errors: action.payload,
loading: false,
}
default:
return state;
}
}
the reducer of the fetching, updating state,
src\redux\counterReducer.js
import { types } from "./types";
const initialState = {
value: 0
}
export const counterReducer = (state = initialState, action) => {
switch (action.type) {
case types.adicionar:
return {
...state,
value: state.value + 1
}
case types.resetear:
return {
...state,
value: 0
}
case types.restar:
return {
...state,
value: state.value - 1
}
default:
return state
}
}
this is the reducer of the counter app, with different approach, types are isolated in another file
src\redux\rootReducer.js
import { combineReducers } from 'redux'
import { counterReducer } from './counterReducer'
import { appReducer } from './app.reducer'
export const rootReducer = combineReducers({
counterReducer,
appReducer
})
the rootReducer for gathering the reducers
src\redux\sagas.js
import { put, takeLatest, call } from 'redux-saga/effects'
import { loadPostApi } from './api'
import { loadPostFail, loadPostSuccess } from './app.actions'
export function* onLoadPostStartAsync() {
try {
const response = yield call(loadPostApi)
yield put(loadPostSuccess(response.data))
} catch (error) {
yield put(loadPostFail(error))
}
}
export function* onLoadPost() {
yield takeLatest('LOAD_POST_START', onLoadPostStartAsync)
}
export default function* rootSaga() {
yield ([
onLoadPost(),
])
}
saga onLoadPostStartAsync called by saga onLoadPost inside rootSaga
src\redux\store.js
import { applyMiddleware, compose, createStore } from "redux";
import createSagaMiddleware from 'redux-saga'
import { rootReducer } from "./rootReducer";
import rootSaga from "./sagas";
const sagaMiddleware = createSagaMiddleware()
const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware))
export const store = createStore(rootReducer, enhancer)
sagaMiddleware.run(rootSaga)
this is the store with the redux_devtool_extension, the reducers, and running rootSaga
src\redux\types.js
export const types = {
adicionar: 'ADICIONAR',
resetear: 'RESETEAR',
restar: 'RESTAR'
}
those are the types of the counterApp reducer
src\Counter.js
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
export const Counter = () => {
const dispatch = useDispatch()
const { value } = useSelector(state => state.counterReducer)
const handleAdicionar = () => {
dispatch({ type: 'ADICIONAR' })
}
const handleResetear = () => {
(value !== 0) && dispatch({ type: 'RESETEAR' })
}
const handleRestar = () => {
dispatch({ type: 'RESTAR' })
}
console.log(value)
return (
<div>
<button onClick={handleAdicionar}>Adicionar</button>
{' '}
<button onClick={handleResetear}>Resetear</button>
{' '}
<button onClick={handleRestar}>Restar</button>
<hr />
</div>
)
}
this is the Counter component, it works ok
src\Fetching.js
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { loadPostStart } from './redux/app.actions'
export const Fetching = () => {
const dispatch = useDispatch()
const fetchPost = () => {
dispatch(loadPostStart())
}
const state = useSelector(state => state.appReducer)
console.log(state)
return (
<>
<h1>Fetching from https://jsonplaceholder.typicode.com</h1>
<button onClick={fetchPost}>Fetching</button>
{
!state.loading && state.posts.map((post) => (
<li key={post.id}><h2>{post.title}</h2></li>
))
}
</>
)
}
the Fetching component click on the button calls fetchPost function who dispatch loadPostStart() function which is the same of dispatching {type: 'LOAD_POST_START'}, but nothing happens here when clicking, not fetch nothing from here https://jsonplaceholder.typicode.com/posts
src\index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { store } from './redux/store';
import { Provider } from "react-redux";
import { Unificator } from './Unificator';
ReactDOM.render(
<Provider store={store}>
<Unificator />
</Provider>,
document.getElementById('root')
);
component Unificator has Counter and Fetching component
src\Unificator.js
import React from 'react'
import { Counter } from './Counter'
import { Fetching } from './Fetching'
export const Unificator = () => {
return (
<div>
<Counter />
<Fetching />
</div>
)
}
as you can see is about of two reducers, one is the famous counter, and the another one is the fetching issue, do not know what is happening that is not fetching the data
obviously, i am doing something wrong here...don t see where
Axio returns promise, You need to capture that and return. Please try replacing below code.
export const loadPostApi = async () => {
await axios.get(`https://jsonplaceholder.typicode.com/posts`)
.then((response) => {
console.log('Response', response);
return response;
})
.catch((error) => {
console.log('error', error);
})
}

Is there a way to dispatch an action on Component Mount?

I'm using Firebase for user authentication and I want to use the onAuthStateChanged() to make a user persist even after refreshing the browser. I'm also using redux-sagas to handle async operations.
Index.jsx file:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import './index.css';
import './App.scss';
import store from './store';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
serviceWorker.unregister();
App.jsx:
import React, { Component } from 'react';
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';
import { connect } from 'react-redux';
import Navbar from './components/navbar';
import routes from './routes';
import { actionTypes } from './components/signin/actionTypes';
const { VERIFY_REQUEST } = actionTypes;
const mapDispatchToProps = {
VERIFY_REQUEST,
};
class App extends Component {
render() {
return (
<Router>
<Navbar />
<Switch>
{routes.map(route => (
<Route
key={route.path}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</Router>
);
}
}
export default connect(null, mapDispatchToProps)(App);
My sagas generator function binded to the action type:
function onAuthState() {
return new Promise((resolve, reject) => {
loginToFirebase.auth().onAuthStateChanged(user => {
if (user) {
console.log(user);
resolve(user);
} else {
reject(new Error('Ops!'));
}
});
});
}
function* verifyUserAuth() {
try {
const LOGIN_API_URL = process.env.REACT_APP_USER_AUTH_API;
const { user } = yield onAuthState();
console.log(user);
const userInfo = { userAuth: user, userType: 'user' };
const config = { headers: { 'Content-Type': 'application/json' } };
const body = JSON.stringify(userInfo);
const response = yield axios.post(LOGIN_API_URL, body, config);
if (response.status === 200) {
const { data: { info } } = response.data;
yield put({ payload: info, type: VERIFY_SUCCESS });
} else yield put(loginError(response.status));
} catch (error) {
yield put(loginError(error));
}
}
export default function* watchUserLoginAction() {
yield takeEvery(VERIFY_REQUEST, verifyUserAuth);
}
Everytime I check my redux tools, I don't see the action being fired on component mount.
You can use the componentDidMount lifecycle method whenever you want to do something after the component is mounted. Modify your mapDispatchToProps
const mapDispatchToProps = dispatch => {
return {
verifyRequest: () => { dispatch( {type : VERIFY_REQUEST} ) }
};
};
and then call verifyRequest from componentDidMount
componentDidMount = () =>{
this.props.verifyRequest()
}
Also, it is better to create action creators instead of directly dispatching the action, like so
export const verifyRequestAction = () => {
return {
type: VERIFY_REQUEST
}
}
and then
const mapDispatchToProps = dispatch => {
return {
verifyRequest: () => { dispatch(verifyRequestAction()}
};
};

'dispatch' is not defined - mapDispatchToProps not passing through Router

I am getting a dispatch is not defined error from 'shopfront' code. I believe it is because i'm not passing the properties down to the next level but I'm not sure if that is correct or not. I want to be able to pass the dispatch function through to the product.actions code correctly.
I have tried to narrow down the problem as much as possible by removing unnecessary code. I have a user reducer that is working correctly but I don't know why this product reducer isn't
// products.reducer
const initialState = {
products: null,
error: null
};
const ProductReducer = (state = initialState, action) => {
let newState = null;
switch(action.type){
case "GET_ALL_PRODUCTS": newState = {
...state,
products: action.products
};
return newState;
case "GET_ALL_PRODUCTS_FAIL": newState = {
...state,
error: action.error
};
return newState;
default: return state;
}
};
export default ProductReducer;
// index
import React from "react";
import ReactDOM from "react-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import { register } from "./serviceWorker";
import { createStore, combineReducers, applyMiddleware, compose } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import UserReducer from "./store/reducers/users.reducers";
import ProductReducer from "./store/reducers/products.reducer";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
const appReducer = combineReducers({
usersRed: UserReducer,
productsRed: ProductReducer
});
const logger = (store) => {
return next => {
return action => {
console.log("Middleware dispatching ");
console.log(action);
const result = next(action);
console.log("Middleware next state ");
console.log(store.getState());
return result;
};
};
};
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const appStore = createStore(appReducer, composeEnhancers(applyMiddleware(logger, thunk)));
const app = (
<BrowserRouter>
<Provider store={appStore}>
<App />
</Provider>
</BrowserRouter>
);
ReactDOM.render(app, document.getElementById("root"));
register();
// shopfront
import React, { Component } from "react";
import { Container, Row, Col, InputGroup, InputGroupAddon, Button } from "reactstrap";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import { Alert } from "reactstrap";
import * as actionMethods from "../../store/actions/index.actions";
import Product from "../../components/Product/Product";
class Shopfront extends Component {
state = {
onAlert: false,
internalError: null
};
componentDidMount() {
this.props.loadAllProducts(5);
console.log("component_did_mount_run")
}
render() {
let ProductsList = <h1>No Products Yet!</h1>;
if (this.props.products !== null) {
ProductsList = this.props.products.map(Product => {
return <Product
key={Product.id}
title={Product.name}
excerpt={Product.description}
medialink={Product.permalink}
ProductId={Product.id}
/>;
});
}
return (
<Container>
{ProductsList}
</Container>
);
}
};
const mapStateToProps = (state) => {
return {
products: state.productsRed.products,
error: state.productsRed.error
};
};
const mapDispatchToProps = (dispatch) => {
return {
loadAllProducts: (perpage) => { dispatch(actionMethods.loadAllProducts(perpage)) }
};
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Shopfront));
// index.actions
export {
loadAllProducts
} from "./product.actions";
// product.actions
import wcapi from "../../axios-wp";
export const loadAllProducts = (perpage) => {
wcapi.get("products", {
per_page: perpage,
})
.then((response) => {
// Successful request
let productsRes = response.data;
dispatch({ type: "GET_ALL_PRODUCTS", products: productsRes });
})
.catch((err) => {
// Invalid request, for 4xx and 5xx statuses
dispatch({ type: "GET_ALL_PRODUCTS_FAIL", error: err });
})
}
Thank you for your help!
it is because you are not returning dispatch from your loadAllProducts action
/ product.actions
import wcapi from "../../axios-wp";
export const loadAllProducts = (perpage) => (dispatch) => { //make this change
wcapi.get("products", {
per_page: perpage,
})
.then((response) => {
// Successful request
let productsRes = response.data;
dispatch({ type: "GET_ALL_PRODUCTS", products: productsRes });
})
.catch((err) => {
// Invalid request, for 4xx and 5xx statuses
dispatch({ type: "GET_ALL_PRODUCTS_FAIL", error: err });
})
}
I have ever faced such this issue before. Then I used return dispatch => {} like this:
export const loadAllProducts = perpage => {
return dispatch => {
wcapi.get("products", {
per_page: perpage,
})
.then((response) => {
// Successful request
let productsRes = response.data;
dispatch({ type: "GET_ALL_PRODUCTS", products: productsRes });
})
.catch((err) => {
// Invalid request, for 4xx and 5xx statuses
dispatch({ type: "GET_ALL_PRODUCTS_FAIL", error: err });
})
}
}

React-Redux call to firebase isnt showing data?

Im running into a problem with my redux to firebase connection i believe.
Trying to grab all jobs from users in firebase.
Have my store setup, action and reducer, not really sure where i am going wrong here so i must be overlooking something, nothing is showing up in console and i put a console.log call on my action and nothing shows still.
my action :
// Grab all Jobs
export const getJobs = (jobs) => ({
type: 'GET_JOBS',
jobs
});
export const startGetJobs = () => {
return(dispatch, getState) => {
const uid = getState().auth.uid;
return database.ref(`users/${uid}/jobs`)
.once('value')
.then((snapshot) => {
const jobs =[];
console.log(jobs);
//Parse the data using snapshot
snapshot.forEach((childSnapshot) => {
jobs.push({
id: childSnapshot.key,
...childSnapshot.val()
});
});
dispatch(getJobs(jobs));
});
};
};
my reducer file :
const jobReducerDefaultState = [];
export default (state= jobReducerDefaultState, action) => {
switch(action.type) {
case 'ADD_JOB':
return [
...state,
action.job
];
case 'REMOVE_JOB':
return state.filter(({ id }) => id !== action.id);
case 'EDIT_JOB':
return state.map((job) => {
if(job.id === action.id) {
return {
...job,
...action.updates
};
} else {
return job;
}
});
case 'GET_JOBS':
return action.jobs;
default:
return state;
}
};
my redux store file :
import { createStore, combineReducers, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import jobsReducer from '../reducers/jobs';
import filtersReducer from '../reducers/filters';
import authReducer from '../reducers/auth';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default () => {
const store = createStore(
combineReducers({
jobs: jobsReducer,
filters: filtersReducer,
auth: authReducer
}),
composeEnhancers(applyMiddleware(thunk))
);
return store;
};
And trying to call that with this component :
import React from 'react';
import { connect } from 'react-redux';
import JobDataItem from './JobDataItem';
import { startGetJobs } from '../actions/jobs';
class JobData extends React.Component {
ComponentDidMount() {
this.props.startGetJobs();
}
render() {
return (
<div>
{this.props.jobs.map((job) => {
return <JobDataItem key={job.id} company={job.company}/>
})}
</div>
);
};
};
const mapDispatchToProps = (dispatch) => {
return {
startGetJobs: (jobs) => dispatch(startGetJobs(jobs))
}
}
export default connect(undefined, mapDispatchToProps)(JobData);
which passes that data to the jobDataItem component to render to screen below:
import React from 'react';
import { Link } from 'react-router-dom';
const JobDataItem = ({ id, company}) => (
<div>
<Link to={`/edit/${id}`}>
<h3>{company}</h3>
</Link>
</div>
);
export default JobDataItem;
my firebase db formats like:
users/
user-uid/
jobs/
job-uid/
company:"Company Name",
jobTitle:"jobTitle:,
And so on...
Expected output is "Company Name" but nothing shows up at all. i try to just call props.jobs.length and it shows up as 0 as well.
EDITED
1. “Called startGetJobs in componentDidMount(),
2. “Changed props.jobs.map((job)... to this.props.jobs.map((job)...
I now get props is undefined error in console and nothing still appears on screen.

How to change the state of e.target.value with Redux/React?

This is part of an extensive application so i will just post the relevant parts. I'm trying to implement an e.target.value onChange from my App.js up to the index.js file of the application. The app loads but breaks the moment a value is inserted in the input field and I'm referring to the mapDispatchToProps function:
App.js
import React, { Component } from 'react';
import Navbar from './components/Navbar';
import ToggleLayout from './components/ToggleLayout';
import { connect } from 'react-redux';
class App extends Component {
render() {
return (
<div>
<Navbar
searchTerm={this.props.searchItunes.searchTerm}
onSearchChange={(e) =>this.props.onSearchChange(e.target.value)}
/>
<ToggleLayout
switchLayout={()=> this.props.switchLayout()}
grid={this.props.toggle.grid}
/>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
toggle: state.booleanReducer,
searchItunes: state.searchItunesReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
switchLayout: () => {
dispatch({
type:"GRID"
});
},
onSearchChange: (e) => {
dispatch({
type:"SEARCHTERM",
payload:e.target.value
});
}
};
};
export default connect(mapStateToProps,mapDispatchToProps)(App);
and the index file is as below:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'react-redux';
import { createStore, combineReducers, applyMiddleware } from 'redux';
const booleanReducer = (state = { grid:true, additionalPages:false }, action) => {
if (action.type === "GRID"){
return state = {
...state,
grid:!state.grid
}
}
return state;
};
const searchItunesReducer = (state = { searchTerm:'', itunes:null }, action) => {
if (action.type === 'SEARCHTERM'){
return state = {
...state,
searchTerm:action.payload
}
}
}
const store = createStore(combineReducers({booleanReducer,searchItunesReducer}));
console.log(store.getState());
store.subscribe(() =>{
console.log("store updated!", store.getState());
});
registerServiceWorker();
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
The switch layout function works as intended, so could you tell me what I'm doing wrong with the onSearchChange function?
In your Navbar component you're passing onSearchChange prop as an anonymous function that calls this.props.onSearchChange with e.target.value - so you're passing the exact value to the callback while the callback in mapDispatchToProps you're defining onSearchChange as a function that accepts the change event. That's why you're getting an error when you change the search input value.
You have 2 options here, either you pass just an event to onSearchChange in the Navbar component:
<Navbar searchTerm={this.props.searchItunes.searchTerm}
onSearchChange={this.props.onSearchChange} />
or change the onSearchChange signature so that it accepts only the final value:
onSearchChange: (value) => {
dispatch({
type: "SEARCHTERM",
payload: value
});
}
<Navbar
searchTerm={this.props.searchItunes.searchTerm}
onSearchChange={(e) =>this.props.onSearchChange(e.target.value)}
/>
In the method call you are sending the value of the event, your search term, so in mapDispatchToProps you don't need to send the whole event again, you just need to send the string you are setting in onSearchChange, because in your reducer you are setting the full action payload to the searchTerm reducer attribute.
const mapDispatchToProps = (dispatch) => {
return {
switchLayout: () => {
dispatch({
type:"GRID"
});
},
onSearchChange: (term) => {
dispatch({
type:"SEARCHTERM",
payload: term,
});
}
};
};

Resources