score:1

Accepted answer

Here is a simple app just in one file :) You can read the comments and try to understand what is going on. It will give you an idea of how you can keep a status state in your store, dispatch an action and update your state.

As you will see, Post and List components does not have any state. They are just dumb components. The parent one, Posts component renders them.

You can see a working example here, fork and play it. There are separate directories and files for this example. I just put everything in one file to move it here properly. I can't promise to keep the sandbox too long, so you may want to fork it immediately :)

PS: This is a midnight fun. It may include not the best practices :)

import React from "react";
import ReactDOM from "react-dom";
import { Provider, connect } from "react-redux";
import { createStore, combineReducers } from "redux";

// To use styles in a single file. You can use a .css file to define those
// and use className instead of styles in the Post component

const styles = {
  post: { border: "1px solid gray", marginTop: "-1px" },
  show: { backgroundColor: "silver"}, 
}

// Posts is the parent component. It renders Post and List component by
// mapping the posts.

class Posts extends React.Component {
  // This method changes the status state by passing a post to
  // the action creator

  handleStatus = post => this.props.changeStatus(post);

  // This method maps the posts and renders Post components for each post.
  // We are passing the post and isActive boolean.

  getPosts() {
    return this.props.posts.map(post => {
      // We are getting the isActive by checking the status state
      // if it has our post's id.
      const isActive = this.props.status[post.id];
      return <Post key={post.id} post={post} isActive={isActive} />;
    });
  }

  // This method renders our List items, li's. Again, we are passing the
  // post and our handleStatus method to change the status.

  getList() {
    return this.props.posts.map(post => (
      <List key={post.id} post={post} handleStatus={this.handleStatus} />
    ));
  }

  render() {
    return (
      <div>
        {this.getPosts()}
        <ul>{this.getList()}</ul>
      </div>
    );
  }
}

// Post is a stateless, dumb component. It just renders the post item.

const Post = props => {
  const { id, title } = props.post;
  const { isActive } = props;

  // We check the isActive and if it is true then add a show class.

  let classes = styles.post;
  if (isActive) {
    classes = { ...classes, ...styles.show };
  }

  return (
    <div style={classes}>
      <p>ID: {id} </p>
      <p>Title: {title}</p>
    </div>
  );
};

// List is a stateless, dumb component just renders li's. But, it has
// handleStatus prop. By onClick and using another method, we are
// passing our post back to the parent, to the handleStatus method

const List = props => {
  const { post, handleStatus } = props;
  const changeStatus = () => handleStatus(post);
  return <li onClick={changeStatus}>{post.title}</li>;
};

// We open our state to our component.

const mapStateToProps = state => ({
  posts: state.posts.posts,
  status: state.posts.status,
});

// This is our action creator, takes the post as parameter and
// use it as the payload.

const changeStatus = post => ({
  type: types.CHANGE_STATUS,
  post,
});

// Connecting our component.

const ConnectedPosts = connect(
  mapStateToProps,
  { changeStatus },
)(Posts);

// Just defining our action creator types, avoiding magic strings.   

const types = {
  CHANGE_STATUS: "CHANGE_STATUS",
};

// This is our reducer. We have one posts property and one status in our state.

const initialState = {
  posts: [
    { id: "1", title: "foo" },
    { id: "2", title: "bar" },
    { id: "3", title: "baz" },
  ],
  status: {},
};

// Our reducer takes the post and adds post's id in our status state.
// In the initial state they are all undefined, so not true. In the first
// click, we change to true for each post. If we click again, we change it to
// false without mutating our original state.

const posts = (state = initialState, action) => {
  switch (action.type) {
    case types.CHANGE_STATUS: {
      const {
        post: { id },
      } = action;
      const newStatus = { ...state.status, [id]: !state.status[id] };
      return { ...state, status: newStatus };
    }
    default:
      return state;
  }
};

// Our store.    

const rootReducer = combineReducers({
  posts,
});

const store = createStore(rootReducer);

// Rendering our app.

const rootElement = document.getElementById("root");

ReactDOM.render(
  <Provider store={store}>
    <ConnectedPosts />
  </Provider>,
  rootElement,
);

score:1

You can just store selectedID (or index) and use simple condition, sth like this:

class Posts extends Component {

  render() {
    return (
      <div>
        {this.props.posts.map((post, index) => 
          <Post isActive={this.props.selectedIDX === index} post={post} />
        )}
      </div>
    );
  }
}    

// connect with posts and selectedIDX 


class List extends Component {
  constructor (props) {
    super(props)
    this.onClickHandler = this.onClickHandler.bind(this);
  }

  onClickHandler = (id) => {
    this.props.actionToSetSelectedIDX( id );
  }

  render() {
    return (
      <ul>
        {this.props.posts.map((post, index) => 
          <li onClick={(e, index) => this.onClickHandler(index) }>{post.name}</li>
        )}
      </ul>
    );
  }

 // connect with posts, selectedIDX and actionToSetSelectedIDX

Related Query

More Query from same tag