Ре-рендеринг компонента реакции после нажатия кнопки «Нравится» (с Redux) - PullRequest
0 голосов
/ 22 декабря 2018

У меня есть следующий компонент React, который показывает все сообщения пользователей с помощью метода "renderPosts".Под ней есть кнопка «Нравится / не нравится» о том, понравился ли зарегистрированному в данный момент пользователю пост.

Однако, когда я нажимаю кнопку «Мне нравится», компонент не выполняет повторную визуализацию, чтобы метод «renderPosts» создал непохожую кнопку, а строка «Мне нравится» изменяется, как и ожидалось.Только когда я перехожу к другому компоненту, а затем возвращаюсь к этому компоненту, кнопка непохожего отображается и наоборот.

Можно ли как-нибудь исправить это с помощью Redux в моем приложении?Я попробовал this.forceUpdate после события onClick, но все еще не работает ...

Также я попытался создать новый Reducer, называемый "likers", в соответствии с robinsax, который в основном получает массив пользователей, которым нравится конкретный пост.и импортировал его как реквизиты в компонент, но получил

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

Когда приложение впервые попадает на главную страницу (PostIndex), возможно, потому что this.props.likers по-прежнему пустой объект, возвращаемый из редуктора

Вот код для моего создателя действия:

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 голосов
/ 28 декабря 2018

Как говорится, используйте 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
    });
}

}

0 голосов
/ 22 декабря 2018

Как отмечалось в других ответах, вам нужно использовать redux-thunk или redux-saga для выполнения асинхронных вызовов, которые обновляют ваш редуктор.Я лично предпочитаю redux-saga.Вот базовая реализация React, Redux и Redux-Saga.

Redux-Saga использует функции генератора JavaScript и yield для достижения цели обработки асинхронных вызовов.

Ниже вы увидите много знакомого кода React-Redux, ключевые части Redux-Saga:

  • watchRequest - Функция генератора, которая отображаетдиспетчеризация действий для функций генератора
  • loadTodo - функция генератора, вызываемая от watchRequest до yield значения из асинхронного вызова, и отправка действия для редуктора
  • 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>
0 голосов
/ 22 декабря 2018

Проблема в вашем создателе действия.

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-thunk или redux-saga для работы с асинхронными действиями.

0 голосов
/ 22 декабря 2018

Для использования асинхронных действий вам необходимо использовать промежуточное программное обеспечение 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);
0 голосов
/ 22 декабря 2018

Похоже, что схожая / непохожая функция записи не вызывает изменений в ваших state или props, поэтому компонент не рендерится повторно.

Вы должны изменить структуру данных, которую вы храните, чтобы значение post.likedBy.includes(currentUser) было включено в один из них, или forceUpdate() компонент после вызовов likePost и unlikePost.

Пожалуйста, сделайте это первым способом, чтобы я мог спать по ночам.Наличие на компоненте render() тех вещей, которые не входят в props или state, отрицательно сказывается на цели использования React.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...