我可以一键更改100个组件的状态吗?

时间:2018-09-16 20:45:07

标签: javascript reactjs redux

我在一页上有100个Post组件。每个都有状态isActive。我还在此页面上单独列出了这些帖子的标题。如果我单击列表中的项目,那么我想将Post Component的状态更改为isActive:true,将其余组件的状态更改为isActive:false。

我在Post组件之上有Posts组件,因此我应该在Posts组件中遍历所有Post组件,并将更改状态设置为isActive:false,而无需当前单击的Post组件?

在这种情况下不是那么夸张吗?有效率吗?我使用Redux,所以也许会有所帮助?

class Posts extends Component {
  state = { 
    isActive: this.props.isActive
  }

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

const mapStateToProps = state => {
  return {
    posts: state.posts.posts,
    isActive: state.posts.isActive
  }
}

export default connect(mapStateToProps, null)(Posts);

class Post extends Component {
  state = { 
    isActive: this.props.isActive
  }

  onClick = () => {
    this.setState(state => ({
      isActive: !state.isActive
    }));
  }

  render() { 
    return (
      <div className={this.state.isActive ? "show" : ""} onClick={this.onClick}>
        Post
      </div>
    );
  }
}

export default Post;

class List extends Component {

  onClick = () => {
    //here I would like to set isActive:true to current clicked Post and isActive:false to rest of Posts
  }

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

const mapStateToProps = state => {
  return {
    posts: state.posts.posts
  }
}

export default connect(mapStateToProps, null)(List);

2 个答案:

答案 0 :(得分:1)

您可以只存储selectedID(或索引)并使用简单条件,例如:

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

答案 1 :(得分:1)

这里是一个简单的应用程序,仅位于一个文件中:)您可以阅读评论并尝试了解发生了什么。它将使您了解如何在商店中保持status状态,调度动作并更新状态。

您将看到,PostList组件没有任何状态。它们只是愚蠢的组件。父组件Posts呈现它们。

您可以看到一个有效的示例here,进行分叉并播放。此示例有单独的目录和文件。我只是将所有内容都放在一个文件中,以将其正确移动到此处。我不能保证将沙箱保留得太久,所以您可能要立即分叉它:)

PS:这是午夜的乐趣。它可能不包括最佳做法:)

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,
);