2

I am slowly learning React and also learning to implement it with Redux. But I seem to have hit a road block. So this is what I have so far.

/index.jsx

import './main.css'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App.jsx'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import ShoppingList from './reducers/reducer'

let store = createStore(ShoppingList)

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('app')
)

/actions/items.js

import uuid from 'node-uuid'

export const CREATE_ITEM = 'CREATE_ITEM'
export function createItem(item) {
  return {
    type: CREATE_ITEM,
    item: {
      id: uuid.v4(),
      item,
      checked: false
    }
  }
}

/reducers/reducer.js

import * as types from '../actions/items'
import uuid from 'node-uuid'

const initialState = []

const items = (state = initialState, action) => {
  switch (action.type) {
    case types.CREATE_ITEM:
      return {
        id: uuid.v4(),
        ...item
      }
    default:
      return state;
  }
 }

 export default items

/reducers/index.js
UPDATE:

import { combineReducers } from 'redux'
import items from './reducer'

const ShoppingList = combineReducers({
  items
})

export default ShoppingList

/components/Item.jsx

import React from 'react';
import uuid from 'node-uuid'

export default class Item extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isEditing: false
    }
  }

  render() {
    if(this.state.isEditing) {
      return this.renderEdit();
    }

    return this.renderItem();
  }

  renderEdit = () => {
    return (
      <input type="text"
        ref={(event) => 
          (event ? event.selectionStart = this.props.text.length : null)
        }
        autoFocus={true}
        defaultValue={this.props.text}
        onBlur={this.finishEdit}
        onKeyPress={this.checkEnter} 
      />
    )
  };

  renderDelete = () => {
    return <button onClick={this.props.onDelete}>x</button>;
  };

  renderItem = () => {
    const onDelete = this.props.onDelete;

    return (
      <div onClick={this.edit}>
        <span>{this.props.text}</span>
        {onDelete ? this.renderDelete() : null }
      </div>
    );
  };

  edit = () => {
    this.setState({
      isEditing: true
    });
  };

  checkEnter = (e) => {
    if(e.key === 'Enter') {
      this.finishEdit(e);
    }
  };

  finishEdit = (e) => {
    const value = e.target.value;

    if(this.props.onEdit) {
      this.props.onEdit(value);

      this.setState({
        isEditing: false
      });
    }
  };
}

/components/Items.jsx

import React from 'react';
import Item from './Item.jsx';

export default ({items, onEdit, onDelete}) => {
  return (
    <ul>{items.map(item =>
      <li key={item.id}>
        <Item 
          text={item.text} 
          onEdit={onEdit.bind(null, item.id)}
          onDelete={onDelete.bind(null, item.id)}
        />
      </li>
    )}</ul>
  );
}
//  UPDATE: http://redux.js.org/docs/basics/UsageWithReact.html
// Is this necessary?
const mapStateToProps = (state) => {
  return {
    state
  }
}

Items = connect(
  mapStateToPros
)(Items) // `SyntaxError app/components/Items.jsx: "Items" is read-only`

//////////////////////////////////////
// Also tried it this way. 
//////////////////////////////////////
Items = connect()(Items)
export default Items // same error as above.

Tried this as well

export default connect(
  state => ({
    items: store.items
  })
)(Items) // `Uncaught TypeError: Cannot read property 'items' of undefined`

UPDATE:

After many attempts @hedgerh in Gitter pointed out that it should be state.items instead. so the solution was

export default connect(
  state => ({
    items: state.items
  })
)(Items)

credits to @azium as well.

/components/App.jsx

export default class App extends React.Component {
  render() {
    return (
     <div>
        <button onClick={this.addItem}>+</button>
          <Items />
      </div>
    ); 
  } 
}

What am I missing here in order to implement it correctly? Right now it breaks saying that Uncaught TypeError: Cannot read property 'map' of undefined in Items.jsx. I guess it makes sense since it doesn't seem to be hooked up correctly. This is the first part of the app, where the second will allow an user to create a many lists, and these lists having many items. I will probably have to extract the methods from Item.jsx since the List.jsx will do pretty much the same thing. Thanks

11
  • You seem to be missing App.jsx in your example.. how is items being passed to <Items /> ? Commented Mar 28, 2016 at 21:53
  • Something needs to be connected to your store.. so either you forgot to show that part or you need to add it somewhere. Commented Mar 28, 2016 at 21:54
  • I think thats what I'm unsure. I have an App.jsx, hold on I'll update my question. @azium Commented Mar 28, 2016 at 21:55
  • Are you using <provider> to pass the store down to it's children? How is the store being passed down? Are you using connect inside your App.jsx? Commented Mar 28, 2016 at 21:59
  • @James111 I am using <Provider> look in my index.js. I do pass the store there too. If I understand it correctly by using Provider and passing the store there, that store would be available to my children components. correct? Now how do i connect the app is a bit fuzzy stil. Commented Mar 28, 2016 at 22:03

1 Answer 1

2

You're missing connect. That's how stuff gets from your store to your components. Read the containers section from the docs http://redux.js.org/docs/basics/UsageWithReact.html

import React from 'react'
import Item from './Item.jsx'

import { connect } from 'react-redux'

let Items = ({items, onEdit, onDelete}) => {
  return (
    <ul>{items.map(item =>
      <li key={item.id}>
        <Item 
          text={item.text} 
          onEdit={onEdit.bind(null, item.id)}
          onDelete={onDelete.bind(null, item.id)}
        />
      </li>
      })
  </ul>
  )
}

export default connect(
  state => ({
    items: state.items
  })
)(Items)

Also you seem to be expecting onEdit and onDelete functions passed from a parent but you're not doing that so those functions will be undefined.

Sign up to request clarification or add additional context in comments.

8 Comments

It says it can't find the store I created my store in my index.jsx Should I create it in a different file? say stores/store.js and then import that here? @azium
You're importing the wrong file in index.jsx. It should be import ShoppingList from './reducers'. Currently you're importing the reducer without calling combineReducers
After your suggested changes i'm getting Uncaught TypeError: Cannot read property 'items' of undefined in my Items.jsx at store.items
export default connect( state => ({ items: state.items }) )(Items)
@Diego I think in @azium's example, it was meant that export default connect(state => ({ items: store.items}))(Items) would actually be this: export default connect( state => ({ items: state.items }) )(Items)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.