单击“赞”按钮(带Redux)后重新渲染react组件

时间:2018-12-22 00:55:07

标签: javascript reactjs redux

我有以下React组件,该组件通过“ renderPosts”方法显示所有用户的帖子。在其下方,有一个喜欢/不同按钮,用于显示当前登录用户是否喜欢该帖子。

但是,当我单击“ like”按钮时,该组件不会重新渲染,以便“ renderPosts”方法创建一个“ like”按钮,并且按预期方式修改了“ like字符串”。只有当我转到另一个组件然后再回到该组件时,才会显示不同的按钮,反之亦然。

无论如何,我可以使用Redux在我的应用程序中解决此问题吗?我在onClick事件之后尝试了this.forceUpdate,但仍然无法正常工作...

根据robinsax的说法,我还尝试创建一个新的Reducer,称为“ likers”,它基本上得到了喜欢特定帖子的用户数组,并将其作为道具导入组件中,但得到了

"this.props.likers.includes(currentUser)" is not a function

应用程序首次进入主页(PostIndex)时,可能是因为this.props.likers仍然是reducer返回的空对象

这是我的动作创建者的代码:

export function likePost(username,postId) {
    // body...
    const request = {
        username,
        postId
    }
    const post = axios.post(`${ROOT_URL}/likePost`,request);
    return{
        type: LIKE_POST,
        payload: post
    }
}
export function unlikePost(username,postId){
    const request = {
        username,
        postId
    }
    const post = axios.post(`${ROOT_URL}/unlikePost`,request);
    return{
        type: UNLIKE_POST,
        payload: post
    }
}

这是我的减速器:

import {LIKE_POST,UNLIKE_POST} from '../actions/index.js';

export default function(state = {},action){
    switch(action.type){
        case LIKE_POST:
            const likers = action.payload.data.likedBy;
            console.log(likers);
            return likers;
        case UNLIKE_POST:
            const unlikers = action.payload.data.likedBy;
            console.log(unlikers);
            return unlikers;
        default:
            return state;
    }
}

由于我是初学者,我将非常感谢您的帮助

import { fetchPosts } from "../actions/";
import { likePost } from "../actions/";
import { unlikePost } from "../actions/";
class PostsIndex extends Component {
    componentDidMount() {
        this.props.fetchPosts();
    }
    renderPost() {
        const currentUser = Object.values(this.props.users)[0].username;
        return _.map(this.props.posts, post => {
            return (
                <li className="list-group-item">
                    <Link to={`/user/${post.username}`}>
                        Poster: {post.username}
                    </Link>
                    <br />
                    Created At: {post.createdAt}, near {post.location}
                    <br />
                    <Link to={`/posts/${post._id}`}>{post.title}</Link>
                    <br />
                    //error here, with this.props.likers being an 
                    //array
                    {!this.props.likers.includes(currentUser) ? (
                        <Button
                            onClick={() => this.props.likePost(currentUser,post._id)}
                            bsStyle="success"
                        >
                            Like
                        </Button>
                    ) : (
                        <Button
                            onClick={() => this.props.unlikePost(currentUser,post._id)}
                            bsStyle="warning"
                        >
                            Unlike
                        </Button>
                    )}{" "}
                    {post.likedBy.length === 1
                        ? `${post.likedBy[0]} likes this`
                        : `${post.likedBy.length} people like this`}
                </li>
            );
        });
    }
function mapStateToProps(state) {
    return {
        posts: state.posts,
        users: state.users,
        likers: state.likers
    };
}
}

5 个答案:

答案 0 :(得分:2)

您需要使用redux-thunk中间件才能使用异步操作。 首先,在创建像这样的商店时添加redux-thunk

import thunk from 'redux-thunk';
const store = createStore(
   rootReducer,
   applyMiddleware(thunk)  
);

然后像这样更改您的方法

export function likePost(username,postId) {
    return function(dispatch) {
        // body...
         const request = {
            username,
            postId
          }
         axios.post(`${ROOT_URL}/likePost`,request)
          .then(res => {
              dispatch({
                 type: LIKE_POST,
                 payload: res
           });
      });
    }
}

,现在在mapStateToProps之后的组件中,定义mapDispatchToProps,

const mapDispatchToProps = dispatch => {
       return {
           likePost: (currentUser,postId) => dispatch(likePost(currentUser, postId)),
           // same goes for "unlike" function
       }
 }

export default connect(mapStateToProps, mapDispatchToProps)(PostsIndex);

答案 1 :(得分:1)

好像“喜欢/不喜欢”帖子功能不会引起您的stateprops中的任何更改,因此该组件不会重新渲染。

您应该更改要存储的数据结构,以使post.likedBy.includes(currentUser)的值包含在其中之一,或者forceUpdate()和{{1}之后的组件中}。

请先做一下,这样我就可以晚上入睡。使组件的likePost受其unlikePostrender()中未包含的事物的影响会破坏使用props的目的。

答案 2 :(得分:1)

问题出在您的动作创建者上。

export function likePost(username,postId) {
    // body...
    const request = {
        username,
        postId
    }
    // this is an async call
    const post = axios.post(`${ROOT_URL}/likePost`,request);
    // next line will execute before the above async call is returned
    return{
        type: LIKE_POST,
        payload: post
    }
}

因为您的状态可能永远不会更新,并保持在初始值。

您需要使用redux-thunkredux-saga来执行异步操作。

答案 3 :(得分:1)

如其他答案所述,您需要使用redux-thunkredux-saga进行异步调用以更新减速器。我个人更喜欢redux-saga。这是React,Redux和Redux-Saga的基本实现。

Redux-Saga使用JavaScript generator functionsyield完成了处理异步调用的目标。

下面您将看到很多熟悉的React-Redux代码,Redux-Saga的关键部分如下:

  • watchRequest-生成器函数,可将调度动作映射到生成器函数
  • loadTodo-从watchRequestyield的生成器函数中调用异步调用中的值,并为reducer调度动作
  • getTodoAPI-发出fetch请求的常规函数​​
  • applyMiddleware-来自Redux的用于将Redux-Saga与createStore
  • 连接

const { applyMiddleware, createStore } = Redux;
const createSagaMiddleware = ReduxSaga.default;
const { put, call } = ReduxSaga.effects;
const { takeLatest } = ReduxSaga;
const { connect, Provider } = ReactRedux;

// API Call
const getTodoAPI = () => {
  return fetch('https://jsonplaceholder.typicode.com/todos/1')
    .then(response => {
      return response.json()
        .then(response =>  response);
    })
    .catch(error => {
      throw error;
    })
};

// Reducer
const userReducer = (state = {}, action) => {
  switch (action.type) {
    case 'LOAD_TODO_SUCCESS':
      return action.todo;
    default:
      return state;
  }
};

// Sagas, which are generator functions
// Note: the asterix
function* loadTodo() {
  try {
    const todo = yield call(getTodoAPI);
    yield put({type: 'LOAD_TODO_SUCCESS', todo});
  } catch (error) {
    throw error;
  }
}

// Redux-Saga uses generator functions,
// which are basically watchers to wait for an action
function* watchRequest() {
  yield* takeLatest('LOAD_TODO_REQUEST', loadTodo);
}

class App extends React.Component {
  render() {
    const { data } = this.props;
      return (
        <div>
          <button onClick={() => this.props.getTodo()}>Load Data</button>
          {data ?
            <p>data: {JSON.stringify(data)}</p>
            : null
          }
        </div>
      )
    }
}
// Setup React-Redux and Connect Redux-Saga
const sagaMiddleware = createSagaMiddleware();
const store = createStore(userReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(watchRequest);

// Your regular React-Redux stuff
const mapStateToProps = (state) => ({ data: state }); // Map the store's state to component's props
const mapDispatchToProps = (dispatch) => ({ getTodo: () => dispatch({type: 'LOAD_TODO_REQUEST'}) })  // wrap action creator with dispatch method
const RootComponent = connect(mapStateToProps, mapDispatchToProps)(App);

ReactDOM.render(
  <Provider store={store}>
    <RootComponent />
  </Provider>,
  document.getElementById('root')
);
<script src="https://npmcdn.com/babel-regenerator-runtime@6.3.13/runtime.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.1/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/6.0.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux-saga/0.16.2/redux-saga.min.js"></script>
<div id="root"></div>

答案 4 :(得分:0)

正如他们所说,使用redux-thunk或redux-saga。如果您是redux的新手,我更喜欢redux-thunk,因为它比redux-saga更易于学习。您可以像这样重写代码

export function likePost(username,postId) {
// body...
const request = {
    username,
    postId
}
const post = axios.post(`${ROOT_URL}/likePost`,request);
return dispatch => {
    post.then(res => {
      dispatch(anotherAction) //it can be the action to update state
    });
}

}