如何使用TypeScript在React Native中设置Redux?

时间:2019-09-04 08:04:21

标签: typescript react-native redux react-navigation

我正在学习React Native,并使用TypeScript正确设置了React Navigation。现在我要添加Redux。我尝试实现与React(web)相同的方式。

但是似乎在React Native中缺少一些东西,TodoItem组件和TodoScreen出现了,但是我无法添加或删除。

// MyTypes.d.ts

declare module 'MyTypes' {
  import { StateType, ActionType } from 'typesafe-actions';
  // 1 for reducer, 1 for action creators
//   export type ReducerState = StateType<typeof import('../reducers').default>;
  export type ReducerState = StateType<typeof import('../reducers/todo').default>;
  export type RootAction = ActionType<typeof import('../actions').default>;
}
// actions/todo.tsx

import { action } from 'typesafe-actions';

export enum TodoActionTypes {
  ADD = 'ADD',
  DELETE = 'DELETE'
}

export const todoActions = {
  add: (item: string) => action(TodoActionTypes.ADD, item),
  delete: (idx: number) => action(TodoActionTypes.DELETE, idx)
};
// reducers/todo.tsx

import * as MyTypes from 'MyTypes';
import { TodoActionTypes } from '../actions/todo';

interface TodoModel {
  count: number;
  list: string[];
}

export const initialState: TodoModel = {
  count: 2,
  list: ['Do the laundry', 'Do the dishes']
};

export const todoReducer = (
  state: TodoModel = initialState,
  action: MyTypes.RootAction
) => {
  switch (action.types) {
    case TodoActionTypes.ADD: {
      return {
        ...state,
        count: state.count + 1,
        list: [...state.list, action.payload]
      };
    }
    case TodoActionTypes.DELETE: {
      const oldList = [...state.list];
      oldList.splice(action.payload, 1);
      const newList = oldList;

      return {
        ...state,
        count: state.count - 1,
        list: newList
      };
    }
    default:
      return state;
  }
};
// Store.tsx

import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger';
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
import { todoReducer } from './reducers/todo';

const middleware: any = [thunk];
if (process.env.NODE_ENV !== 'production') {
  middleware.push(createLogger());
}

// @ts-ignore
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const rootReducer = combineReducers({
  todo: todoReducer
});

const Store = createStore(
  rootReducer,
  composeEnhancer(applyMiddleware(...middleware))
);

export default Store;
// App.tsx

import React from 'react';
import { Provider } from 'react-redux';
import Store from './Store';
import AppContainer from './navigation/AppContainer';

export default function App() {
  return (
    <Provider store={Store}>
      <AppContainer />
    </Provider>
  );
}
// TodoItem.tsx

import React from 'react';
import { View, Text, Button } from 'react-native';

interface Props {
  item: string;
  idx: number;
  handleDelete: (idx: number) => void;
}

const TodoItem: React.FC<Props> = props => {
  return (
    <View>
      <Text>{props.item}</Text>
      <Button title="X" onPress={() => props.handleDelete(props.idx)} />
    </View>
  );
};

export default TodoItem;
// TodoScreen.tsx

import React, { Component } from 'react';
import { Dispatch } from 'redux';
import * as MyTypes from 'MyTypes';
import { connect } from 'react-redux';
import { View, Button, Text, TextInput } from 'react-native';
import TodoItem from '../components/TodoItem';
import { TodoActionTypes } from '../actions/todo';

interface State {
  todoInput: string;
}

interface Props {
  count: number;
  todoList: string[];
  addTodo: (item: string) => object;
  deleteTodo: (idx: number) => object;
}

class TodoScreen extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      todoInput: ''
    };
  }

  handleTextChange = (text: any) => {
    this.setState({
      todoInput: text
    });
  };

  handleAddButtonClick = () => {
    this.props.addTodo(this.state.todoInput);
    this.setState({
      todoInput: ''
    });
  };

  handleDeleteButtonClick = (idx: number) => {
    console.log('Deleting: ', idx);
    this.props.deleteTodo(idx);
  };

  render() {
    let todoJSX: JSX.Element[] | JSX.Element;

    if (!this.props.todoList.length) {
      todoJSX = <p>Nothing to do</p>;
    } else {
      todoJSX = this.props.todoList.map((item, idx) => {
        return (
          <TodoItem
            key={idx}
            item={item}
            idx={idx}
            handleDelete={this.handleDeleteButtonClick}
          />
        );
      });
    }

    return (
      <View>
        {todoJSX}
        <TextInput
          editable={true}
          value={this.state.todoInput}
          placeholder={'New To Do Here'}
          onChangeText={this.handleTextChange}
        />
        <Button title="Add To Do" onPress={this.handleAddButtonClick} />
      </View>
    );
  }
}

const MapStateToProps = (store: MyTypes.ReducerState) => {
  return {
    count: store.todo.count,
    todoList: store.todo.list
  };
};

const MapDispatchToProps = (dispatch: Dispatch<MyTypes.RootAction>) => ({
  addTodo: (item: string) =>
    dispatch({ type: TodoActionTypes.ADD, payload: item }),
  deleteTodo: (idx: number) =>
    dispatch({ type: TodoActionTypes.DELETE, payload: idx })
});

export default connect(
  MapStateToProps,
  // @ts-ignore
  MapDispatchToProps
)(TodoScreen);

对不起,这堆代码。如果您需要,这里是完整的回购清单:https://github.com/Mexman2Rowen/so-nav-dux我试图将其降至最低。

0 个答案:

没有答案