this is file list.js
...
class List extends React.Component {
render() {
return (
...
{this.props.todoList.map((todo, index) => <Item {...todo} key = {index}/>)} // err this code
...
);
}
};
const mapStateToProps = (state) => {
return {
todoList: state.todos
};
};
...
this is file rootReducers.js
const initialState = {
todos : [
{ id: 1, name: "Khoa" },
{ id: 2, name: "Khoai" },
{ id: 3, name: "Kha" }
],
currentName : ''
}
const TodoList = (state = initialState, action) => {
switch (action.type) {
case "ADD_TODO":
return [...state.todos, { id: state.todos.length + 1, name: action.text }];
default:
return state;
}
};
...
this is action.js
export const addTodo = (text) =>{
return {
type : 'ADD_TODO',
text
}
}
this is file fromCreate.js
...
const mapDispatchToProps = (dispatch) => {
return {
addTodo: (text) => dispatch(addTodo(text))
};
};
...
i'm loading data success but, when i excute event addtoto it message : Cannot read property 'map' of undefined. Help me
Your reducer code seems wrong. You need to return full new state slice from it not just an array.
const TodoList = (state = initialState, action) => {
switch (action.type) {
case "ADD_TODO":
return {
...state,
todos: [...state.todos, { id: state.todos.length + 1, name: action.text }]
};
default:
return state;
}
};
Related
As an initial state I use array of objects:
export default{
items: [
{
Date: 1,
Operation: 'revenue',
}
]
}
In component I dispatch the action, which must update one element in object of array: "Operation"
const mapDispatchToProps = (dispatch) => {
return {
selectOperation: (input) => dispatch({type: app.SELECT, payload: input})
}
};
class OperationSelect extends React.Component {
// constructor
handleChange(event) {
this.props.selectOperation({
key: 'Operation',
value: event.target.value
});
};
// render() logic
}
export default connect(null, mapDispatchToProps)(OperationSelect)
Reducer:
import initialState from '../constants/initialState';
import { app } from '../constants/types';
export default function update(state = initialState, action) {
switch (action.type) {
case app.SELECT:
return {
...state,
[action.payload.key]: state.items.map(
(item, i)=> i===0 ? {...item, Operation: action.payload.value}
: item
)
};
default:
return state;
}
}
But, when I select new value and application run handleChange, dispatch the action, run reducer, the state in store keeps the old value of "Operation".
What am I doing wrong?
This is I think what you need to do:
first add an id property to your items and then do something like this:
export default {
items: [
{
id: 0,
Date: 1,
Operation: "revenue",
},
],
};
class OperationSelect extends React.Component {
// constructor
handleChange(event) {
this.props.selectOperation({
key: "Operation", // I think you need to check this and try to findout that you need this or not
value: event.target.value,
id: 0 // 0 is just an example you need to decide how you would implement the id
});
}
// render() logic
}
export default function update(state = initialState, action) {
switch (action.type) {
case app.SELECT:
return {
...state,
items: state.items.map((item, i) =>
i === action.payload.id ? { ...item, Operation: action.payload.value } : item
),
};
default:
return state;
}
}
The problem was in that I did not update in reducer the state of array.
This is a working code:
export default function update(state = initialState, action) {
switch (action.type) {
case app.SELECT:
return {
...state,
items: state.items.map(
(item, i)=> i===0 ? {...item, [action.payload.key]: action.payload.value}
: item
)
};
default:
return state;
}
}
Earlier in return-block I used [action.payload.key] in place, where "items" should have used. So I updated "Operation" in place, where "items" updated.
This is my react redux reducer
const initialState = {
products: [
{
name: 'Icecream',
inCart: false
},
{
name: 'Cake',
inCart: false
}
]
};
export const reducer = (state = initialState, action) => {
switch (action.type) {
case BUY_CAKE:
return {
state
};
default:
return state;
}
};
I want to change the first object's inCart value to true in products array when the BUY_CAKE action is called .
How can I do it ??
Lets say that action.payload is equal to the object {name: 'Cake', inCart: true}, then you can do something like this:
const initialState = {
products: [
{
name: 'Icecream',
inCart: false
},
{
name: 'Cake',
inCart: false
}
]
};
export const reducer = (state = initialState, action) => {
switch (action.type) {
case BUY_CAKE:
const products = return state.products.map(item => {
if(item.name !== action.payload.name) {
return item;
}
return {
...item,
inCart: action.payload.inCart
}
})
return {
...state,
products
}
default:
return state;
}
};
You can simply use a map function where it iterate through all objects and find the payload name and changes inCart to true return the updated object
const initialState = {
products: [
{
name: 'Icecream',
inCart: false
},
{
name: 'Cake',
inCart: false
}
]
};
export const reducer = (state = initialState, action) => {
switch (action.type) {
case BUY_CAKE:
return {
...state,
products:state.products.map(item=>{
if(item.name===action.payloadName)
item.inCart=true
return item
})
};
default:
return state;
}
};
I am following a tutorial trying to learn Redux. I got the first action working, which is a simple GET API call, but am stuck on the next action I'm trying to create. The code looks like the following:
In the Component:
class ShoppingList extends Component {
componentDidMount() {
this.props.getItems();
}
handleClick = id => {
console.log("component " + id);
this.props.deleteItem(id);
};
render() {
const { items } = this.props.item;
return (
<Container>
<ListGroup>
<TransitionGroup className="shoppingList">
{items.map(({ id, name }) => (
<CSSTransition key={id} timeout={500} classNames="fade">
<ListGroupItem>
<Button
className="button1"
color="danger"
size="sm"
onClick={e => this.handleClick(id, e)}
>
×
</Button>
{name}
</ListGroupItem>
</CSSTransition>
))}
</TransitionGroup>
</ListGroup>
</Container>
);
}
}
ShoppingList.propTypes = {
getItems: PropTypes.func.isRequired,
item: PropTypes.object.isRequired,
deleteItem: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
item: state.item
});
export default connect(mapStateToProps, { getItems, deleteItem })(ShoppingList);
In my reducer:
const initialState = {
items: [
{ id: 3, name: "Eggs" },
{ id: 4, name: "Milk" },
{ id: 5, name: "Steak" },
{ id: 6, name: "Water" }
]
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_ITEMS:
return {
...state
};
case DELETE_ITEM:
console.log("reducer");
return {
...state,
items: state.items.filter(item => item.id !== action.id)
};
default:
return state;
}
}
In my actions file:
export const getItems = () => {
return {
type: GET_ITEMS
};
};
export const deleteItem = id => {
console.log("actions");
return {
type: DELETE_ITEM,
payload: id
};
};
However, when I click on the button to try to delete an item from the list, nothing happens. I can see in the Redux console that the action is being dispatched, however it seems to have no effect. Any suggestions?
You have in deleteItem action { type, payload }. Instead you can have { type, id } or using payload in the reducer return statement.
I would do the following - so you are passing the id with the action instead of payload:
export const deleteItem = id => {
console.log("actions");
return {
type: DELETE_ITEM,
id
};
};
Or the best option for later purposes - keep payload just adding id as property:
// action
export const deleteItem = id => {
console.log("actions");
return {
type: DELETE_ITEM,
payload: { id }
};
};
// reducer
case DELETE_ITEM:
// here destructuring the property from payload
const { id } = action.payload;
return {
...state,
items: state.items.filter(item => item.id !== id)
};
I hope this helps!
I need to toggle somehove adding/removing object from redux store. It is like check/uncheck. I have following code:
const list = ({ id, item }) => {
const isChecked = name => items.some(item => item.name === name);
let itemClass = cx({
item: true,
selected: isChecked(name),
});
return (
<li className={itemClass}
onClick={() => click(fullItem)} key={id}>
<div className={styles.name}>
{isChecked(name) ?
(<span><i className={`fa fa-check`}></i>{name}</span>)
: (<span>{name}</span>)
}
</div>
</li>
);
}
export const click = item => ({
type: ADD_ITEM,
payload: item,
});
import {
ADD_ITEM,
} from "../actions";
const initialState = {
items: [],
}
export default (state = initialState, action) => {
switch (action.type) {
case ADD_ITEM:
return {
...state,
items: [action.payload],
};
default:
return state;
}
};
but for now it only work for adding item to store, when I click on item when it is selected, it should remove it from the store. How can I toggle onclick removing/adding object to redux store?
You could try something like this. Changing the ADD_ITEM instead to a TOGGLE_ITEM where you check for existence of the item using something like Array.prototype.find. Adding if it does not exist, and removing it if it does exist:
export const click = item => ({
type: TOGGLE_ITEM,
payload: item,
});
import {
TOGGLE_ITEM,
} from "../actions";
const initialState = {
items: [],
}
export default (state = initialState, action) => {
switch (action.type) {
case TOGGLE_ITEM:
const currentItem = state.items.find(item => item.id === action.payload.id);
if (!currentItem) {
return {
...state,
items: [...state.items, action.payload],
};
} else {
const newItems = state.items.filter(item => item.id !== action.payload.id];
return {
...state,
items: [...newItems]
};
}
default:
return state;
}
};
You may though want to consider having separate add, update, and delete actions, and dispatch the different actions accordingly from your components.
export const click = item => ({
type: TOGGLE_ITEM,
payload: item,
});
import {
TOGGLE_ITEM,
} from "../actions";
const initialState = {
items: [],
}
export default (state = initialState, action) => {
switch (action.type) {
case TOGGLE_ITEM:
// check to see if the item already in our array
// Array.some return true/false
const itemAlreadyExists = state.items.some(item => item.id === action.payload.id)
return {
...state,
// if the item already in our array filter it
// if not just add it
items: itemAlreadyExists
? state.items.filter(item => item.id !== action.payload.id)
: [...state.items, action.payload],
};
default:
return state;
}
};
In my project I'm using tcomb-form-native library to validation a form. Redux working fine but I can't pass value of inputs to reducer. I have to do this, because I want to create array with data from fields.
How can I pass values of my inputs to reducer?
Or maybe it's not possible with this library and I have to use another one?
Form.js
const mapDispatchToProps = dispatch => {
return {
closeExpenseDialog: (value) => dispatch({type: 'CLOSE_EXPENSE_DIALOG'}),
};
};
const mapStateToProps = state => {
return {
value: state.closeExpenseDialog.value,
};
};
const Form = t.form.Form;
const Expense = t.struct({
expense: t.String,
cost: t.Number
});
const options = {
fields: {
expense: {
error: 'This field is required'
},
cost: {
error: 'This field is required'
}
}
};
handleClick = () => {
const value = this._form.getValue();
if (value) {
console.log(value);
this.props.closeExpenseDialog(value);
} else {
console.log('validation failed');
}
}
<Form
type={Expense}
ref={c => this.props._form = c}
options={options}
value={this.props.value}
/>
<ActionButton
onPress={this.props.closeExpenseDialog}
title={title}
/>
Reducer.js
const initialState = {
value: {}
};
const mainReducer = (state = initialState, action) => {
switch (action.type) {
case 'CLOSE_EXPENSE_DIALOG':
console.log('it works')
console.log(state.value) //undefined
default:
return state;
}
};
I needed add onChange() attribute and use it to pass object value to the Reducer.
Form.js
const Expense = t.struct({
expense: t.String,
cost: t.Number
});
const options = {
fields: {
expense: {
error: 'This field is required'
},
cost: {
error: 'This field is required'
}
}
};
handleClick = () => {
const value = this._form.getValue();
if (value) {
this.props.submitExpenseDialog();
}
}
<Form
type={Expense}
ref={c => this._form = c}
options={options}
value={this.props.value}
onChange={this.props.changeExpenseInputs}
/>
<ActionButton
onPress={this.handleClick}
title={title}
/>
Reducer.js
const initialState = {
value: {}
};
const mainReducer = (state = initialState, action) => {
switch (action.type) {
case 'SUBMIT_EXPENSE_DIALOG':
console.log(state.value)
default:
return state;
}
};