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!
Related
I am trying to display the redux state into my react component, but it comes undefined.
I am unable to understand where am I doing the mistake.
I am learning redux by trying a coding on my own by going through the redux documentation.
Main React component
import React, { Component } from 'react';
import Counter from './components/Counter';
import {Provider} from 'react-redux';
import store from './redux/store';
class App extends Component {
render() {
return (
<Provider store={store}>
<div>
<h1>COUNTER APPlICATION</h1>
<Counter />
</div>
</Provider>
)
}
}
export default App;
React Component
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {addNumber} from '../redux/actions/addAction';
import {substractNumber} from '../redux/actions/substractAction';
export class Counter extends Component {
render() {
return (
<div>
<h1>Value:{this.props.value}</h1>
<h1>Add Only Value:{this.props.addOnly}</h1>
<button onClick = {() => this.props.addNumber}>+</button>
<button onClick = {() => this.props.substractNumber}>-</button>
</div>
)
}
}
const mapStateToProps = state => ({
value: state.value
});
export default connect(mapStateToProps, {addNumber, substractNumber})(Counter);
addReducer
import {ADDITION} from '../actions/actionTypes';
const initialState = {
value: 50
}
export default function (state = initialState, action) {
switch(action.type){
case ADDITION:
return{
value: state.value + 2
}
default:
return state
}
}
substractReducer
import {SUBSTRACTION} from '../actions/actionTypes';
const initialState = {
value: 50
}
export default function (state = initialState, action) {
switch (action.type) {
case SUBSTRACTION:
return {
value: state.value - 2
}
default:
return state
}
}
rootReducer
import {combineReducers} from 'redux';
import addReducer from './addReducer';
import substractReducer from './substractReducer';
export default combineReducers({
add: addReducer,
substract: substractReducer
})
store
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers/rootReducer';
import thunk from 'redux-thunk';
export default createStore(rootReducer, applyMiddleware(thunk));
action type
export const ADDITION = 'ADDITION';
export const SUBSTRACTION = 'SUBSTRACTION';
addAction
import {ADDITION} from './actionTypes';
export const addNumber = () => (dispatch) => {
return dispatch({
type: ADDITION,
payload: 2
})
}
substractAction
import {SUBSTRACTION} from './actionTypes';
export const substractNumber = () => (dispatch) => {
return dispatch({
type: SUBSTRACTION,
payload: 2
})
}
You are doing wrong.
you state is just counter value, so don't split into two reducers. You only need two case statement, one for ADD, one for SUBTRACT.
Don't use combineReducer and it you want, use one key like counter for counter reducer
in mapStateToProp, get value like state.counter.value where counter is name of key you used in combineReducer({ counter: counterReducer })
Your button actions/onclick is wrong
import {ADDITION, SUBTRACTION} from '../actions/actionTypes';
const initialState = {
value: 50
}
export default function (state = initialState, action) {
switch(action.type){
case ADDITION:
return
value: state.value + 2
}
case SUBTRACTION:
return{
value: state.value + 2
}
default:
return state
}
}
///// no need to regester 2 reducer, just add one above like this
export default combineReducers({
counter: counterReducer
});
/// In Counter component , mapStateToProp
const mapStateToProps = state => ({
value: state.counter.value
});
// Just pass redux actions to onClick in button like this
<button onClick = {this.props.addNumber}>+</button>
<button onClick = {this.props.substractNumber}>-</button>
When you combineReducers like this:
export default combineReducers({
add: addReducer,
substract: substractReducer
})
Your state tree will look like:
{
add: {
value: 0
},
subtract: {
value: 0
}
}
So you should only have a single reducer in order to reduce over the same value.
I am developing a lottery statistics app that gets data from a csv loaded from an input then I was wanting to read this data to the redux store so I can use it across multiple components.
I have successfully saved the data to the redux store once I import the file and read it through Header.js and using an action, but I am not sure how to access this in other components like e.g. Main.js.
I feel like I am still confused on how react/redux all fits together. I'm sorry if this has been asked before but everything I looked up online I couldn't get to work.
// index.js
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import reducers from "./reducers";
import App from "./components/App";
const store = createStore(reducers, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector("#root")
);
// App.js
import React from "react";
import Header from "./Header";
import Main from "./Main";
const App = () => {
return (
<div>
<Header />
<Main />
<div className="numbers-for-draw"></div>
</div>
);
};
export default App;
// Header.js
import React from "react";
import { CSVReader } from "react-papaparse";
import { fetchData } from "../actions";
import { connect } from "react-redux";
class Header extends React.Component {
constructor(props) {
super(props);
this.fileInput = React.createRef();
}
handleReadCSV = data => {
this.props.fetchData(data);
console.log(this.props.data);
};
handleOnError = (err, file, inputElem, reason) => {
console.log(err);
};
handleImportOffer = () => {
this.fileInput.current.click();
console.log("Got to handleImportOffer");
};
render() {
return (
<header>
<CSVReader
onFileLoaded={this.handleReadCSV}
inputRef={this.fileInput}
style={{ display: "none" }}
onError={this.handleOnError}
/>
<button onClick={this.handleImportOffer}>Import</button>
</header>
);
}
}
//Map what is in the redux store (e.g. state) to props
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps, {
fetchData: fetchData
})(Header);
// Main.js
import React from "react";
import { fetchData } from "../actions";
import { connect } from "react-redux";
const Main = () => {
console.log("In main");
console.log(this.props.data); //Blows up here.
return <div>Main</div>;
};
//Map what is in the redux store (e.g. state) to props
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps, {
fetchData: fetchData
})(Main);
// actions/index.js
export const fetchData = data => dispatch => {
console.log("Action");
const lottoData = {
stringNumbers: [
"one",
"two",
"three",
...
],
allResults: [],
winningNumbers: [],
winningNumbersAsStrings: []
};
const localData = data.data;
localData.shift();
localData.forEach(line => {
const lineObject = {
draw: line[0],
drawDate: line[1],
ballOne: line[2],
ballTwo: line[3],
ballThree: line[4],
ballFour: line[5],
ballFive: line[6],
ballSix: line[7],
bonusBall: line[8],
bonusBall2: line[9],
powerBall: line[10]
};
lottoData.allResults.push(lineObject);
let nums = [];
nums.push(parseInt(line[2]));
nums.push(parseInt(line[3]));
nums.push(parseInt(line[4]));
nums.push(parseInt(line[5]));
nums.push(parseInt(line[6]));
nums.push(parseInt(line[7]));
nums.sort((a, b) => {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
lottoData.winningNumbers.push(nums);
lottoData.winningNumbersAsStrings.push(nums.toString());
});
dispatch({ type: "FETCH_DATA", payload: lottoData });
};
// lottoReducer.js
export default (state = {}, action) => {
switch (action.type) {
case "FETCH_DATA":
return action.payload;
default:
return state;
}
};
// reducers/index.js
import { combineReducers } from "redux";
import lottoReducer from "./lottoReducer";
export default combineReducers({
data: lottoReducer
});
I haven't tested your code, but it seems to me that the only problem is in your Main.js
While you use a function component and not a class, you shouldn't use this to access your props. The following should work as expected:
const Main = (props) => {
console.log("In main");
console.log(props.data);
return <div>Main</div>;
};
//Map what is in the redux store (e.g. state) to props
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps, {
fetchData: fetchData
})(Main);
In your main.js you used functional components so this.props doesn't work there. You must pass props to your component and console.log(props.data).
Please tell me why, when I call this.props.getOnEvents(), an error occurs that “getOnEvents() is not a function”, what’s wrong here and how to fix it?
I’m looking at the console.log and there is this function in the props, but when I try to call, an error flies out that it’s not a function
EventCalendar.js
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import EventCalendarTable from './EventCalendarTable';
import EventCalendarView from './EventCalendarView';
const styles = theme => ({
root: {
width: '80%',
margin: '20px auto 0'
},
});
class EventCalendar extends Component {
constructor(props) {
super(props);
this.state = {
viewEvents: 'table'
};
}
componentDidMount() {
this.props.onGetEvents();
}
changeEventsView = (value) => {
this.setState({ viewEvents: value });
}
render() {
console.log(this.props);
const { classes } = this.props;
return (
<div className={classes.root}>
<EventCalendarView changeEventsView={this.changeEventsView}/>
{
this.state.viewEvents === 'table'
? <EventCalendarTable />
: <div>test</div>
}
</div>
);
}
}
export default withStyles(styles)(EventCalendar);
EventPage/component.jsx
import React from 'react';
import EventCalendar from '../../components/EventCalendar';
import './index.scss';
function Events(props) {
return (
<React.Fragment>
<EventCalendar props={props}/>
</React.Fragment>
);
}
export default Events;
EventPage/container.js
import { connect } from 'react-redux';
import EventsPage from './component';
import { getEvents } from '../../../store/modules/Events/operations';
const mapStateToProps = ({ Events }) => ({
Events
});
const mapDispatchToProps = dispatch => ({
onGetEvents: () => {
console.log(123);
dispatch(getEvents());
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(EventsPage);
Events/actions.js
import * as types from './types';
export const eventsFetch = value => ({
type: types.FETCHING_EVENTS,
payload: value
});
export const setEvents = ({ objById, arrayIds }) => ({
type: types.SET_EVENTS,
payload: {
eventById: objById,
eventsOrder: arrayIds
}
});
Events/types.js
export const FETCHING_EVENTS = 'Events/FETCHING_EVENTS';
export const SET_EVENTS = 'Events/SET_EVENTS';
Events/operation.js
import FetchClient from 'app/utils/FetchClient';
import IdsAndByIds from 'app/utils/IdsAndByIds';
import { eventsFetch, setEvents } from './actions';
export const getEvents = () => async (dispatch) => {
try {
const { data } = await FetchClient.get('/events');
dispatch(setEvents(IdsAndByIds(data)));
dispatch(eventsFetch(false));
} catch (error) {
console.log(error);
}
};
Events/reducer.js
import { createReducer } from 'store/utils';
import * as types from './types';
const usersInitState = {
fetching: true,
events: {
eventById: null,
usersOrder: null
},
error: null
};
const eventsReducer = createReducer(usersInitState)({
[types.FETCHING_EVENTS]: (state, { payload }) => ({
...state,
fetching: payload
}),
[types.SET_EVENTS]: (state, { payload }) => ({
...state,
events: {
...payload
}
})
});
export default eventsReducer;
Events/index.js
import { combineReducers } from 'redux';
import * as eventsListOperations from './operations';
import reducer from './reducers';
const EventsReducer = combineReducers({
eventsList: reducer
});
export default EventsReducer;
export { eventsListOperations };
The issue here is a minor one, Since you are connecting Events component to connect, you are receiveing the prop onGetEvents in that component, Now inside this component you are passing the props by a name props to the EventCalendar component
<EventCalendar props={props}/>
Now the props in EventCalender will contain a key called as props which wil lhave your data but you are trying to access it directly on props which is why it is undefined.
The correct way to pass the props here would be to use spread syntax like
<EventCalendar {...props}/>
I am building a simple app to retrieve some recipes from an API URL.
I am reading the documentation of Thunk to implement it but I cannot understand how to set the async get request.
What is strange is that if I console.log the action passed into the reducer it definitely retrieves the correct object (a list of recipes for shredded chicken).
When I pass the action onto the the reducer, instead, it throws the error:
"Unhandled Rejection (Error): Given action "FETCH_RECIPES", reducer "recipes" returned undefined. To ignore an action, you must explicitly return the previous state. If you want this reducer to hold no value, you can return null instead of undefined."
Is there any error in my action creator? is the API call properly done?
Store
import React from 'react';
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import SearchBar from './components/App';
import thunk from 'redux-thunk';
import 'bootstrap/dist/css/bootstrap.css';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
render(
<Provider store={store}>
<SearchBar />
</Provider>,
document.getElementById('root')
)
Component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import './App.css';
import { Button } from 'reactstrap';
import { Form } from 'reactstrap';
import { bindActionCreators } from 'redux';
import {fetchRecipe } from '../actions';
class SearchBar extends Component {
constructor(props) {
super(props)
this.state = { term: ''};
this.typeRecipe = this.typeRecipe.bind(this)
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onFormSubmit(e) {
e.preventDefault()
this.props.fetchRecipe(this.state.term)
}
typeRecipe(e) {
this.setState({term: e.target.value});
}
render() {
return (
<div className="SearchBar">
<Form onSubmit={this.onFormSubmit}>
<input type='text'
value={this.state.term}
placeholder='ciao'
onChange={this.typeRecipe}
/>
<br/>
<Button id='ciao' className='btn-success'>Submit</Button>
</Form>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchRecipe }, dispatch);
}
export default connect(null, mapDispatchToProps)(SearchBar);
Action creator
import axios from 'axios';
export const FETCH_RECIPES = 'FETCH_RECIPES';
const API_KEY = 'xxx';//not the real one.
export function fetchRecipe() {
const request = axios.get(`http://food2fork.com/api/search?key=${API_KEY}&q=shredded%20chicken`);
return (dispatch) => {
request.then(({data}) =>{
dispatch({ type: FETCH_RECIPES, payload: data})
})
}
}
reducer
import { FETCH_RECIPES } from '../actions';
export default function (state = {}, action) {
switch(action.type) {
case FETCH_RECIPES:
const newState = action.payload.data;
return newState;
default:
return state
}
}
combineReducer (index)
import recipeReducer from '../reducers/recipes_reducer';
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
recipes: recipeReducer
});
export default rootReducer;
Mistake is in the reducer return statement.
export default function (state = {}, action) {
switch(action.type) {
case FETCH_RECIPES:
const newState = {...state, data : action.payload.data};
return newState;
default:
return state
}
}
Here we are adding data key to the reducer state, to access this you can use do this in you container :
export default connect((state)=>{
var mapStateToProps = {};
if(state.recipes.data) {
mapStateToProps['recipes'] = state.recipes.data
}
return mapStateToProps;
}, mapDispatchToProps)(SearchBar);
and recipes data will be available as this.props.recipes.
Hi All I am new to redux. I am creating a sample app as below:
entry point: index.js
import 'babel-polyfill'
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import inboundApp from './reducers'
import App from './components/App'
let store = createStore(inboundApp)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
)
/components/App.js
import React from 'react'
import HeaderContainer from '../containers/HeaderContainer'
import LoginForm from '../containers/LoginForm'
const App = () => (
<div>
<HeaderContainer />
<LoginForm />
</div>
)
export default App
/containers/LoginForm.js
import React from 'react'
import { connect } from 'react-redux'
import { login } from '../actions'
let LoginForm = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(login(input.value))
input.value = ''
}}>
<input ref={node => {
input = node
}} />
<button type="submit">
Login
</button>
</form>
</div>
)
}
LoginForm = connect()(LoginForm)
export default LoginForm
/actions/index.js
export const login = (supplierId) => {
return {
type: 'LOGIN',
supplierId
}
}
/containers/HeaderContainer.js
import { connect } from 'react-redux'
import Header from '../components/Header'
const mapStateToProps = (state) => {
return {
supplierId: state.supplierId
}
}
const HeaderContainer = connect(
mapStateToProps,
null
)(Header)
export default HeaderContainer
/components/Header.js
import React, { PropTypes } from 'react'
const Header = ({ supplierId}) => {
return (
<div>
<span>Your Supplier ID: </span> {supplierId}
</div>
)
}
export default Header
/reducers/loginForm.js
const loginForm = (state = '', action) => {
switch (action.type) {
case 'LOGIN':
return Object.assign({}, state, {
supplierId: action.supplierId
})
default:
return state;
}
}
export default loginForm
/reducers/index.js
import { combineReducers } from 'redux'
import loginForm from './loginForm'
const inboundApp = combineReducers({
loginForm
})
export default inboundApp
The problem is my presentation component Header does not get update by the action LOGIN which is firing by click on the button in the LoginForm.js.
would you please help me to find what am I missing? what's wrong with this code?
thanks
I think you try to get the supplierId, from the wrong namespace and your default state of loginForm is not good. Try like that:
const loginForm = (state = {supplierId: ''}, action) => {
switch (action.type) {
case 'LOGIN':
return Object.assign({}, state, {
supplierId: action.supplierId
})
default:
return state;
}
}
And the connect
const mapStateToProps = (state) => {
return {
supplierId: state.loginForm.supplierId
}
}