将react-redux连接到无状态组件。函数内部的属性不会更新

时间:2019-09-16 07:48:56

标签: react-native redux react-redux redux-thunk

问题:

我有一个非常简单的待办事项应用程序。有一个动作-添加待办事项。添加任务时,我模拟使用setTimeout将其发送到服务器。

当我从服务器收到响应时,我立即检查是否有错误,以避免进一步的操作。在有状态组件中,一切正常,在无状态组件中,一切无效。

请参阅代码以更好地理解问题。

环境:

"react": "16.8.6",
"react-native": "0.60.5",
"react-redux": "^7.1.1",
"redux": "^4.0.4",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0"

№1.状态组件:

import React, {Component} from 'react';
import {View, Button, ActivityIndicator} from 'react-native';
import {connect} from 'react-redux';
import {addTodo as addTodoAction} from '../redux/reducer';

class MainScreen extends Component {
  todoGenerator = () => ({
    id: new Date().getTime(),
    text: 'Pls help me ' + new Date().getTime(),
  });

  sendTodoToServer = async () => {
    const todo = this.todoGenerator();
    const {addTodo} = this.props;
    await addTodo(todo);

    // this
    const {error} = this.props;
    if (error) {
      console.log('error', error);
    }
  };

  render() {
    const {isLoading} = this.props;

    return (
      <View>
        <Button title="Generate todo" onPress={this.sendTodoToServer} />
        {isLoading && <ActivityIndicator />}
      </View>
    );
  }
}

export default connect(
  state => ({
    todos: state.todos,
    error: state.error,
    isLoading: state.isLoading,
  }),
  {
    addTodo: addTodoAction,
  },
)(MainScreen);

№1.状态组件。控制台:

如您所见,

 const {error} = this.props;
 if (error) {
     console.log('error', error);
 }

正在工作。好的,让我们继续介绍功能组件

enter image description here

№2.具有Redux连接的无状态组件:

import React from 'react';
import {ActivityIndicator, Button, View} from 'react-native';
import {connect} from 'react-redux';
import {addTodo as addTodoAction} from '../redux/reducer';

const MainScreenFC = ({isLoading, addTodo, error}) => {
  const todoGenerator = () => ({
    id: new Date().getTime(),
    text: 'Pls help me ' + new Date().getTime(),
  });

  const sendTodoToServer = async () => {
    const todo = todoGenerator();
    await addTodo(todo);

    if (error) {
      console.log('error', error);
    }
  };

  return (
    <View>
      <Button title="Generate todo" onPress={sendTodoToServer} />
      {isLoading && <ActivityIndicator />}
    </View>
  );
};

export default connect(
  state => ({
    todos: state.todos,
    error: state.error,
    isLoading: state.isLoading,
  }),
  {
    addTodo: addTodoAction,
  },
)(MainScreenFC);

№2.具有Redux连接的无状态组件。控制台:

该错误未在控制台中显示,尽管它在减速器中

enter image description here

№3.具有redux HOOKS的无状态组件:

import React from 'react';
import {ActivityIndicator, Button, View} from 'react-native';
import {connect, shallowEqual, useDispatch, useSelector} from 'react-redux';
import {addTodo as addTodoAction} from '../redux/reducer';

const MainScreenReduxHooks = () => {
  const todos = useSelector((state: AppState) => state.todos, shallowEqual);
  const error = useSelector((state: AppState) => state.error, shallowEqual);
  const isLoading = useSelector(
    (state: AppState) => state.isLoading,
    shallowEqual,
  );

  const dispatch = useDispatch();

  const todoGenerator = () => ({
    id: new Date().getTime(),
    text: 'Pls help me ' + new Date().getTime(),
  });

  const sendTodoToServer = async () => {
    const todo = todoGenerator();
    await dispatch(addTodoAction(todo));

    if (error) {
      console.log('error', error);
    }
  };

  return (
    <View>
      <Button title="Generate todo" onPress={sendTodoToServer} />
      {isLoading && <ActivityIndicator />}
    </View>
  );
};

export default connect(
  state => ({
    todos: state.todos,
    error: state.error,
    isLoading: state.isLoading,
  }),
  {
    addTodo: addTodoAction,
  },
)(MainScreenReduxHooks);

№3.具有redux HOOKS的无状态组件。控制台:

与第二个示例相同。

问题:

  1. 可以将redux连接到无状态组件吗?
  2. 如何使第二个和第三个示例的工作方式与第一个相同?

其他代码:

App.js

import React from 'react';
import {Provider} from 'react-redux';
import {MainScreen, MainScreenFC, MainScreenReduxHooks} from './src/screens';
import store from './src/redux';

const App = () => {
  return (
    <Provider store={store}>
      <MainScreenFC />
    </Provider>
  );
};

export default App;

store.js:

import {applyMiddleware, createStore} from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import rootReducer from './reducer';

export default createStore(rootReducer, applyMiddleware(thunk, logger));

reducer.js:

const ADD_TODO_REQUEST = 'ADD_TODO_REQUEST';
const ADD_TODO_SUCCESS = 'ADD_TODO_SUCCESS';
const ADD_TODO_FAILURE = 'ADD_TODO_FAILURE';

const initialState = {
  todos: [],
  isLoading: false,
  error: undefined,
};

export const addTodo = data => async dispatch => {
  dispatch({
    type: ADD_TODO_REQUEST,
    payload: {
      isLoading: true,
    },
  });

  try {
    const todo = await new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('Ooops, error');
      }, 3000);
    });

    dispatch({
      type: ADD_TODO_SUCCESS,
      payload: {
        todo,
        isLoading: false,
      },
    });
  } catch (e) {
    dispatch({
      type: ADD_TODO_FAILURE,
      payload: {
        isLoading: false,
        error: e,
      },
    });
  }
};

export default function(state = initialState, {type, payload}) {
  switch (type) {
    case ADD_TODO_REQUEST: {
      return {
        ...state,
        isLoading: true,
      };
    }
    case ADD_TODO_SUCCESS: {
      return {
        ...state,
        isLoading: false,
        todos: [...state.todos, payload.todo],
      };
    }
    case ADD_TODO_FAILURE: {
      return {
        ...state,
        isLoading: false,
        error: payload,
      };
    }
    default:
      return state;
  }
}

1 个答案:

答案 0 :(得分:0)

我认为代码的问题在于您试图等待组件中的响应,这对于有状态和无状态组件都是一个坏主意。我建议您做的是在中间件中的某个地方处理错误(redux-thunkredux-saga等)。在这种情况下,您的组件应该只代表数据,并且如果您必须显示错误,只需从props中获取它,我相信它存储在redux中的某个位置。

无论如何,无状态组件都不应该是异步函数,因为异步函数的结果是一个承诺,而不是一个组件。有一些类似async-reactor的库,但就我个人而言,我更喜欢采用另一种方法。

如果您告诉我您的用例,一旦遇到错误您想怎么做,我会给您一个更有用的答案。

更新

export const addTodo = data => dispatch => {
  // tell your application that request is sending,
  // so you can handle it in UI (show a progress indicator)
  dispatch({
    type: ADD_TODO_REQUEST,
    payload: data
  });

  try {
    const response = await createTodo(data);

    dispatch({
      type: ADD_TODO_SUCCESS,
      payload: response
    });

    // here you can dispatch navigation action as well

  } catch (error) {
    dispatch({
      type: ADD_TODO_FAILURE,
      error
    });

    // and here you can dispatch action with a toast
    // to notify users that something went wrong
  }
};