Array inside redux state is undefined - reactjs

here I'm facing a little problem with react - redux .....
I'm getting state attribute (array) undefined.... What might be the reason?
Here is my Reducers.js code ..
const initialState = {
cList: [], //I'm getting this array undefined...
selectedCatagory: "",
}
export const reducerForCatagoryList = (state = initialState , action) => {
switch(action.type){
case "CATAGORY_LIST":
let lis = [...state.cList,action.payload];
return Object.assign({},state,{ cList: lis });
default:
return state;
}
}
Actions.js code is:
export const catagoryList = (list) => {
return {
type: "CATAGORY_LIST",
payload: list
}
}
And my Component's code is:
//This array is just for testing purpose....
const list = [{
cName: 1,
heading: "Heading",
link: ""
},
{
cName: 2,
heading: "Heading2",
link: ""
}]
const mapStateToProps = (state) => {
console.log("state is: ",state);
return {
cList: state.cList
}
}
const mapDispatchToProps = (dispatch) => {
return {
catagoryList: (list) => {
dispatch(catagoryList(list));
}
}
}
class MainPage extends Component{
render(){
console.log("Props are: ",this.props)
return(
<div id = "Cards-div">
{this.props.catagoryList(list)}
{
createCards(this.props.cList) //I want to send Array inside redux store (cList) to this......
}
</div>
)
}
}
export default connect(mapStateToProps,mapDispatchToProps)(MainPage);
So, Plz can someone explain me what's going wrong here?

Related

state and props are undefined

Hi Im fairly new to coding. I am using react redux and created a store, reducers etc. I cant get anything to render to the screen and when I console.log this.props it comes up an empty array. The data Im dealing with is an array of objects I set the initialstate to an array of objects. I connected all the components with connect and mapstateto props function, there is an empty object in the first arguement of the connect and the second arguement is the componenet. When I look at my component tree it looks fine but my state is undefined and cant figure out why? Here is my code. Thank you in advance.
export const FETCH_SMURFS_START ="FETCH__SMURFS_START"
export const FETCH_SMURFS_SUCCESS = "FETCH_SMURFS_SUCCESS"
export const FETCH_SMURFS_FAILURE ="FETCH_SMURFS_FAILURE"
export const ADD_SMURF_START = "ADD_SMURF_START"
export const ADD_SMURF_SUCCESS = "ADD_SMURF_SUCCESS"
export const ADD_SMURF_FAILURE = "ADD_SMURF_FAILURE"
export const getSmurfData = () =>dispatch=>{
dispatch({type:FETCH_SMURFS_START})
console.log(FETCH_SMURFS_START)
axios.get(' http://localhost:3333/smurfs')
.then((res)=>{
console.log(res.data)
dispatch({type:FETCH_SMURFS_SUCCESS, payload:res.datay})
})
.catch((err)=> {
dispatch({type:FETCH_SMURFS_FAILURE, payload:err.message})
})
}
export const putSmurfData = () =>dispatch=>{
dispatch({type:ADD_SMURF_START})
console.log(ADD_SMURF_START)
dispatch({ADD_SMURF_SUCCESS})
axios.put(' http://localhost:3333/smurfs')
.then((res)=>{
dispatch({type:ADD_SMURF_SUCCESS, payload:res.data})
})
.catch((err)=> {
dispatch({type:ADD_SMURF_FAILURE, payload:err.message})
})
}
class SmurfDisplay extends React.Component {
componentDidMount() {
getSmurfData();
}
render() {
return (
<>
<div>
{this.props.newSmurfData.map((smurf, index) => (
<div>
<h4 key={smurf.id}></h4>
<p> {index}</p>
<p>{smurf.description}</p>
<p>{smurf.nickname}</p>
<p>{smurf.name}</p>
<p>{smurf.position}</p>
</div>
))}
</div>
</>
);
}
}
const mapStateToProps = (state) => {
return {
newSmurfData: [
{
error:state.error,
id: state.id,
name: state.name,
position: state.position,
nickname: state.nickname,
description: state.description,
},
],
};
};
export default connect(mapStateToProps, {})(SmurfDisplay);
class Smurf extends React.Component {
render() {
console.log(this.props);
return (
<>
{this.props.smurfData.map(function(smurf,index) {
return(
<div>
<h4 key={index}></h4>
<p>{smurf.description}</p>
<p>{smurf.nickname}</p>
<p>{smurf.name}</p>
<p>{smurf.position}</p>
</div>
)
})}
</>
)
}
}
const mapStateToProps = (state) =>{
return{
smurfData:[{
error:state.error,
id:state.id,
name:state.name,
position:state.position,
nickname:state.nickname,
description:state.description
}]
}
}
export default connect(mapStateToProps,{})(Smurf)
xport const initialState = {
error:"",
isLoading: false,
smurfData : [{
id:"",
name:"",
position:"",
nickname:"",
description:""
}],
error:"",
isAddingSmurf:false,
newSmurfData:[{
id:"",
name:"",
position:"",
nickname:"",
description:""
}],
}
export const reducer = (state = initialState,action) =>{
switch(action.type){
case FETCH_SMURFS_START:
return{
...state,
isLoading:true,
error:""
}
case FETCH_SMURFS_SUCCESS:
return{
...state,
isLoading:false,
smurfData:action.payload
}
case FETCH_SMURFS_FAILURE:
return{
...state,
isLoading:false,
error:"there was an error getting your smurfs"
}
case ADD_SMURF_START:
return{
...state,
isAddingSmurf:false,
error:""
}
case ADD_SMURF_SUCCESS:
return{
...state,
isAddingSmurf:true,
error: "",
newSmurfData:action.payload
}
case ADD_SMURF_FAILURE:
return{
...state,
isAddingSmurf:false,
addingError:"Error"
}
default:
return state
}
}
export default reducer;
when you want to use this.props in your class Component you need to use super(props) inside the constructor:
constructor(props){
super(props)
}

How to dispatch state of texinput onchangetext to reducer using mapDispatchToProps

I want the states of my textinput to be dispatched to the reducer and change the state of of that reducer by the new state. I also want to be able to view these changed states in a new screen (globally) using mapStateToProps. I am roughly new to redux and I have tried everything possible, but I can seem to get it to work.
Is there any way to do that? I researched and found examples but not the way I want to do it.
I clarify my code is just an example so that you understand what I want to do, do not take it as a guide as I do not know if it works that way
Below are the important part of my code:
Process.Js
import { connect } from 'react-redux';
import {
receiverNameChange,
receiverPhoneChange,
totalParcelsChange } from './actions';
class Process extends Component {
static navigationOptions = {
header: null,
}
constructor(props) {
super(props);
this.state = {
totalParcels: 1,
receiverPhone: '',
receiverName: '',
};
}
onChangeTotalParcels(number) {
const totalParcels = parseInt(number);
if (number.length === 0) {
this.setState({ totalParcels: '' });
} else {
this.setState({ totalParcels });
}
this.props.totalParcelsChange(number);
}
onChangeReceiverNumber = (receiverPhone) => {
this.setState({
receiverPhone
});
this.props.receiverPhoneChange(receiverPhone);
}
onChangeReceiverName = (receiverName) => {
this.setState({
receiverName
});
this.props.receiverNameChange(receiverName);
}
render() {
return (
<View style={styles.AndroidSafeArea}>
<InputField
placeholder={"Enter receiver's name"}
onChangeText={this.onChangeReceiverName}
value={this.state.receiverName}
/>
<InputField
keyboardType={'phone-pad'}
onChangeText={this.onChangeReceiverNumber}
value={this.state.receiverPhone.toString()}
/>
<InputField
keyboardType={'phone-pad'}
onChangeText={this.onChangeTotalParcels}
value={this.state.totalParcels.toString()}
/>
</View>
);
}
}
const mapDispatchToProps = (dispatch) => ({
totalParcelsChange: number => {
dispatch(totalParcelsChange(number));
},
receiverNameChange: receiverName => {
dispatch(receiverNameChange(receiverName));
},
receiverPhoneChange: receiverPhone => {
dispatch(receiverPhoneChange(receiverPhone));
},
});
export default connect(null, mapDispatchToProps)(Process);
Receive.js
import { connect } from 'react-redux';
import {
receiverNameChange,
receiverPhoneChange,
totalParcelsChange } from './actions';
class Receive extends Component {
static navigationOptions = {
header: null,
}
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<View style={styles.AndroidSafeArea}>
<Text>{this.props.receiverNameChange}</Text>
<Text>{this.props.receiverPhoneChange}</Text>
<Text>{this.props.totalParcelsChange}</Text>
</View>
);
}
}
export default connect(null, null)(Receive);
Reduce.js
import { GET_TOTAL_PARCELS, GET_RECEIVER_NAME, GET_RECEIVER_PHONE } from './actions/types';
const initialState = {
receiverName: '',
receiverPhone: '',
number: 0,
};
const ProcessReducer = (state = initialState, action) => {
switch (action.type) {
case GET_TOTAL_PARCELS:
return {
...state,
number: action.payload
};
case GET_RECEIVER_PHONE:
return {
...state,
receiverPhone: action.payload
};
case GET_RECEIVER_NAME:
return {
...state,
receiverName: action.payload
};
default:
return state;
}
};
export default ProcessReducer;
Actions
import { GET_RECEIVER_NAME, GET_RECEIVER_PHONE, GET_TOTAL_PARCELS } from './types';
export const receiverNameChange = (receiverName) => {
return {
type: GET_RECEIVER_NAME,
payload: receiverName
};
};
export const receiverPhoneChange = (receiverPhone) => {
return {
type: GET_RECEIVER_PHONE,
payload: receiverPhone
};
};
export const totalParcelsChange = (number) => {
return {
type: GET_TOTAL_PARCELS,
payload: number
};
};
Kindly correct me where necessary.
I in order for your component in Receive.js to access the state from redux, you should have a mapStateToProps, use it like so:
class Receive extends Component {
...
render() {
return (
<View style={styles.AndroidSafeArea}>
<Text>{this.props.receiverName}</Text>
<Text>{this.props.receiverPhone}</Text>
<Text>{this.props.number}</Text>
</View>
);
}
}
}
const mapStateToProps = state => {
return {
receiverName: state.receiverName,
receiverPhone: state.receiverPhone,
number: state.number
}
}
export default connect(mapStateToProps, null)(Receive);
To have a single source of truth in your Process.Js, you should remove the local state in your Process component and replace the values same as we did in Receive component and you can remove setState.
Hope this helps your problem.

React-Redux's mapStateToProps working in unexpected way

I have a component which shows a list of names fetched from the Redux store. The component looks like this:
class Details extends React.Component {
constructor (props) {
super(props);
}
render () {
const listName = [...this.props.listName];
return (
<div className="container detailsBox">
<p>Details Grid:</p>
{listName ? (
listName.map((el, index) => {
return (
<li key={index}>
{el}
</li>
)
})
): null}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
listName: state.listName
}
}
export default connect(mapStateToProps)(Details);
Notice here I only map the listName from the store. But the code does not display the <li> with the elements of listName even when logging in console shows listName is populated with data
But data shows as expected when the name is also fetched from the store, with this modification in mapStateToProps:
const mapStateToProps = (state) => {
return {
listName: state.listName,
name: state.name
}
}
I am befuddled as well as curious to know why the code behaves in this unexpected way? What am I missing here?
The reducer code looks like this:
import { UPDATE_NAME, ADD_NAME } from '../actions/addName.action';
const initialState = {
name: '',
listName: new Set()
}
function rootReducer (state = initialState, action) {
switch (action.type) {
case ADD_NAME:
return {
name: '',
listName: state.listName.add(action.payload)
}
case UPDATE_NAME:
return {
...state,
name: action.payload
}
default:
return state;
}
}
export default rootReducer;
and the actions like this:
export const ADD_NAME = 'ADD_NAME';
export const UPDATE_NAME = 'UPDATE_NAME';
export function addName (data) {
return {
type: ADD_NAME,
payload: data
}
}
export function updateName (name) {
return {
type: UPDATE_NAME,
payload: name
}
}
This is happens because you mutate the state. When you mutate listNames it still points to the same object in memory, and react thinks that nothing is changed, while the contents of listNames is changed. You should return a new object.
case ADD_NAME:
return {
name: '',
listName: new Set([...state.listName, action.payload]),
}
But it is not recommended to use Sets in reducers. Instead you can store your listNames as an Array and use lodash union function to have only unique values.
import { union } from 'lodash';
const initialState = {
name: '',
listName: []
}
// some code
case ADD_NAME:
return {
name: '',
listName: union(state.listNames, action.payload)
}
You can instead use an object to hold the listName and then recreate the structure of your state into a new object and then return.
const initialState = {
name: '',
listName: {}
}
case ADD_NAME:
return {
name: '',
listName: {...state.listName, action.payload}
}

Filter deletes all state inside the Redux store

I have a little problem with filter of the Redux iteams store with reducers case DELETE_TODO.
So the problem is happen when I try to delete some of the elements inside the todo list. Deleting is work, but per click on some separate element to delete it, the whole todo list is gone. I cannt understand why.
I make the several tests to understand where is the problem located. What I got:
The action work good, because the value come from it into reducers normally. Filter work.
When the list in app had been deleted and I try to add some other todos, the app start crash with error - TypeError: Cannot read property 'todos' of undefined.
Plese, help someone if you can, because I'm already empty... :(
/* REDUCERS */
import { combineReducers } from 'redux'
import { ADD_TODO, ADDED_BUTTON, TOGGLE_BUTTON, EDIT_TODO, DELETE_TODO, FILTER_TODO_UP, FILTER_TODO_DOWN } from '../Variables/Variables'
const initialState = {
iteams: [{
todos:[],
buttons:[]
}]
}
function TodoApp(state, action) {
if (typeof state === 'undefined') {
return initialState;
}
switch (action.type) {
case ADD_TODO:
console.log('Reduce',action.text);
return Object.assign({}, state, {
iteams: [{
todos: [
...state.iteams[0].todos,
{
id: action.id,
text: action.text,
}
],
buttons: [
...state.iteams[0].buttons,
{
id: action.id,
text: action.text,
done: false
}
]
}]
});
case DELETE_TODO:
return Object.assign({}, {
iteams: state.iteams.filter(iteam => {
iteam.todos.id !== parseInt(action.id)
})
});
default:
return state;
}
}
export default TodoApp
/* ACTIONS */
import { ADD_TODO } from '../Variables/Variables'
let nextTodoId = 0;
function AddTodo(text) {
console.log('Action', text);
return {
type: ADD_TODO,
id: nextTodoId++,
text,
done: false
}
};
function DeleteTodo(id) {
return {
type: DELETE_TODO,
id
}
};
export { AddTodo }
/* CONTAINER */
import { connect } from 'react-redux';
import TodoList from '../Components/TodoList/TodoList';
import { DeleteTodo } from '../Actions/AddTodo'
const mapStateToProps = state => ({
iteams: state.iteams
});
const mapDispatchToProps = dispatch => ({
todoFormDelete: todo => dispatch(DeleteTodo(todo))
});
export default connect(
mapStateToProps,
mapDispatchToProps)(TodoList)
/* COMPONENT */
import React, {Fragment} from 'react';
import TodoIteam from '../TodoIteam/TodoIteam'
import ButtonToggle from '../ButtonToggle/ButtonToggle'
class TodoList extends React.Component {
handleDelete = (e) => {
let target = e.target;
let closestDelete = target.closest('span');
let closestEdit = target.closest('button');
if (closestDelete) {
let index = closestDelete.parentNode.getAttribute('index');
console.log('index', index);
this.props.todoFormDelete(index);
} else {
return
}
}
render(props) {
return (
<Fragment>
{console.log('Hi', this.props.store.getState().iteams)}
<div onClick={this.handleDelete}>
{this.props.iteams.map((iteam, index) => {
return <TodoIteam key={index} {...iteam} />
})}
</div>
</Fragment>
);
}
}
export default TodoList;
im so confused with your initialState. I think it must be this
const initialState = {
iteams: {
todos:[],
buttons:[]
}
}
and the ADD_TODO
case ADD_TODO:
return Object.assign({}, state, {
iteams: {
todos: [
...state.iteams.todos, //Change here
{
id: action.id,
text: action.text,
}
],
buttons: [
...state.iteams.buttons, //Change here
{
id: action.id,
text: action.text,
done: false
}
]
}
})
and in the DELETE_TODO
case DELETE_TODO:
return {
iteams: {
todos: state.iteams.todos.filter(iteam => iteam.id !== parseInt(action.id)),
buttons: state.iteams.buttons.filter(button => button.id !== parseInt(action.id))
}
}
Why is iteams an array? (BTW, didn't you mean 'items'?)
When you add a 'todo', you add it to iteams[0].todos.
However, when you filter in order to delete a 'todo', you go through the iteams array, and not through the todos array...
Seems to me (unless I don't understand what you intend to do) is that that the state should be:
iteams: {
todos:[],
buttons:[]
}
and when you delete an item, you should filter the iteams.todos array, not the iteams array.

Update redux state in two places with one action

I am building a webshop and I´ve run in to some problems. When I click on the buy button I want three things to happen:
Add the product to the redux state (shoppingcartReducer)
Update the sum and quantity in the redux state (shoppingcartReducer)
Render out the sum and quantity in my shoppingCart.js
component
The problem is in the shoppingcartReducer. I dont know how to update the state in two different paths on one action.
return state
.setIn(['products', action.articleNr], addArticle(action, state))
Here I am in the 'products/articleNr' path and manipulating data, but I also wants to update the sum and quantity in './'.
So my alternatives that I thought of are maybe to have some sort of middleware thing in the shoppingcartActions but I don´t really know if this is right, nor how to do it!
I appreciate all the help, thanks!
shoppingcartReducer:
import Immutable from 'seamless-immutable'
import { addToShoppingcart, createShoppingCart } from '../actions/shoppingcartActions'
const ADD_TO_SHOPPINGCART = 'ADD_TO_SHOPPINGCART'
const CREATE_SHOPPING_CART = 'CREATE_SHOPPING_CART'
const initialState = Immutable({
sum: 0,
quantity: 0
})
export default function shoppingcartReducer(state = initialState, action) {
switch (action.type) {
case ADD_TO_SHOPPINGCART:
return state
.setIn(['products', action.articleNr], addArticle(action, state))
case CREATE_SHOPPING_CART:
return state
.set(action.id, createCart(action))
}
return state
}
function addArticle(action, state) {
return {
product: action.product
}
}
function createCart(action) {
return {
id: action.id,
}
}
shoppingcartActions:
let nextTodoId = 0
const ADD_TO_SHOPPINGCART = 'ADD_TO_SHOPPINGCART'
const CREATE_SHOPPING_CART = 'CREATE_SHOPPING_CART'
export function addToShoppingcart(product) {
return {
type: ADD_TO_SHOPPINGCART,
articleNr: product.articleNr,
product: product,
}
}
export function createShoppingCart() {
return {
type: CREATE_SHOPPING_CART,
id: 'productNr:'+nextTodoId++,
}
}
ShoppingCart.js:
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as shoppingcartActions from '../../actions/shoppingcartActions'
class ShoppingCart extends Component {
componentWillMount() {
this.state = {
shoppingcartReducer: []
}
}
componentWillReceiveProps(nextProps) {
this.setState({
shoppingcartReducer: nextProps.shoppingcartReducer ? nextProps.shoppingcartReducer : ''
})
}
render() {
const { shoppingcartReducer } = this.props
const { sum, quantity } = shoppingcartReducer
return (
<div className="shoppingCart">
<ul>
<li>Summa: {sum} :-</li>
<li>Antal varor: {quantity}</li>
</ul>
</div>
)
}
}
function mapStateToProps(state) {
return {
shoppingcartReducer: state.shoppingcartReducer
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(shoppingcartActions, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(ShoppingCart)
This worked!
export function addProduct(product) {
return {
type: ADD_PRODUCT,
articleNr: product.articleNr,
product: product,
}
}
export function addSummary(product) {
return {
type: ADD_SUMMARY,
product: product
}
}
export function addToShoppingcart(product) {
return (dispatch) => {
dispatch( addProduct(product))
dispatch( addSummary(product))
}
}

Resources