使用Redux和React-redux在React中进行状态管理

2022-07-29 07:59:25 浏览数 (1)

首先,我们需要使用create-react-app命令行工具安装新的react应用。

运行以下命令安装react app

代码语言:javascript复制
npx create-react-app redux-tutorial

上面的命令将把与React相关的文件下载到“ redux-tutorial”文件夹中。

一旦成功安装,请使用以下命令将工作目录更改为应用程序目录。

代码语言:javascript复制
cd redux-tutorial
npm start

npm start命令用于打开本地开发服务器localhost:3000

安装Redux库

让我们使用以下命令安装reduxandreact-redux库。

代码语言:javascript复制
npm i redux react-redux

redux:Redux用于管理状态

react-redux:用于在react和redux库之间进行绑定。

现在,使用您喜欢的代码编辑器打开“ redux-tutorial”文件夹。

Reducer

Reducer函数是一个纯函数,它采用上一个应用程序状态,type of action并返回下一个状态而不会改变前一个状态。

Redux遵循不变性,这意味着我们不改变应用程序状态,而不是返回 新的应用程序状态。

Redux在单个JavaScript对象中管理整个应用程序状态。

reducerssrc目录中创建一个新文件夹。

在reducers内,文件夹创建一个名为的新文件reducer.js。

代码语言:javascript复制
// reducer.js
const intialState = { name: "reactgo", allNames: []}

const reducer = (state = intialState, action) => {

    if (action.type === "ADDNAME") {
        return {
            allNames: state.allNames.concat(state.name),
            name: ""
        }
    }

    if (action.type === "CHANGE_NAME") {
        return {
            ...state,
            name: action.name
        }
    }

    return state
}

export default reducer;

在上面的代码中,我们定义了带有两个参数state和的reducer函数action。在reducer函数内部,我们添加了两个条件语句。我们的初始状态对象是 { name: "", allNames: []}

将React与Redux连接

代码语言:javascript复制
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux'
import { Provider } from 'react-redux';
import App from './App';
import reducer from './reducers/reducer'

const store = createStore(reducer);

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>
    , document.getElementById('root'));

index.js文件内部,我们从“ redux”库中导入了createStore函数,并从react-redux库中导入 Provider 组件。

我们通过将函数作为参数传递来调用createStore函数,并通过传递store属性reducer<Provider>组件与<App/>组件包装 在一起。

<Provider> 组件使用react context API通过组件树向下传递状态。

从组件访问Redux状态

现在我们可以直接从React组件访问我们的redux状态。

打开App.js文件并添加以下代码。

mapStatetoProps示例

代码语言:javascript复制
import React, { Component } from 'react';
import { connect } from 'react-redux'
import './App.css'

class App extends Component {

  render() {
    return (
      <div className="App">

        <div>
          <input type="text"
            placeholder="Name"
            value={this.props.name} />
      </div>
    );
  }
}


const mapStatetoProps = (state) => {
  return {
    name: state.name,
  }
}

export default connect(mapStatetoProps)(App);

在这里,我们首先导入connectreact-redux库中调用的高阶组件,然后使用state参数定义一个函数mapStatetoProps。

通过使用状态参数, 我们可以访问在reducer函数内部定义的redux状态。

我们mapStatetoProps函数内部定义的任何属性都可以用作App组件内部的props ,例如,在上面的组件中,我们返回的对象带有{name:state.name},这样我们就可以以这样的形式访问组件name内部的该属性。Appthis.props.name

最后,我们需要connect,通过将mapStatetoProps参数作为参数来调用该函数。

如果现在打开浏览器,您会看到“ reactgo”显示在该input字段内。

改变Redux状态

redux状态树是只读的,我们不能直接改变状态。

在redux中,我们只能通过调用dispatch类型为的方法来改变状态action

如果你打开reducer.js文件,你可以看到他们有两种类型,其可用的action为ADDNAMECHANGE_NAME

代码语言:javascript复制
reducer.js
const intialState = { name: "reactgo", allNames: []}

const reducer = (state = intialState, action) => {

    if (action.type === "ADDNAME") {
        return {
            allNames: state.allNames.concat(state.name),
            name: ""
        }
    }

    if (action.type === "CHANGE_NAME") {
        return {
            ...state,
            name: action.name
        }
    }

    return state
}

export default reducer;

如果我们使用类型调用方法ADDNAME,那么我们将返回新状态,并将 name属性值添加到allNames数组中并重置name属性。

让我们看看实际情况。

打开App.js文件,添加以下代码。

mapDispatchtoProps示例

代码语言:javascript复制
// App.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import './App.css'

class App extends Component {


  handleNameChange = (e) => {
    this.props.onChangeName(e.target.value)
  }


  render() {
    return (
      <div className="App">

        <div>
          <input type="text"
            placeholder="Name"
            value={this.props.name} onChange={this.handleNameChange} />

          <button onClick={this.props.onAddName}>Add name</button>

          <ul>
            {this.props.allNames && this.props.allNames.map(name => (
              <li key={name}> {name}</li>
            ))}
          </ul></div>

      </div>
    );
  }
}


const mapStatetoProps = (state) => {
  return {
    name: state.name,
    allNames: state.allNames
  }
}


const mapDispatchtoProps = (dispatch) => {
  return {
    onChangeName: (name) => dispatch({ type: "CHANGE_NAME", name: name }),
    onAddName: () => dispatch({ type: "ADDNAME" }),
  }
}

export default connect(mapStatetoProps, mapDispatchtoProps)(App);

在上面,我们定义了一个mapDispatchtoProps函数,将dispatch方法作为函数参数。

mapDispatchtoProps函数内部,我们返回了一个具有两个属性的对象onChangeNameonAddName

onChangeName:它可以帮助我们了解用户添加dispatch的操作类型CHANGE_NAME和有效负载name属性。

onAddName:它有助于我们dispatch采取行动类型ADDNAME

我们可以App作为来访问组件内部的这两个属性props

现在让我们在浏览器中对其进行测试。

错误处理

我们还可以通过ERROR在reducer函数中创建一个类型来处理错误。

reducer.js使用以下代码更新文件:

代码语言:javascript复制
// reducer.js
const intialState = { name: "reactgo", allNames: [], error: "" }

const reducer = (state = intialState, action) => {

    if (action.type === "ADDNAME") {
        return {
            allNames: state.allNames.concat(state.name),
            name: ""
        }
    }

    if (action.type === "CHANGE_NAME") {
        return {
            ...state,
            name: action.name
        }
    }

    if (action.type === "ERROR") {
        return {
            ...state,
            error: action.error
        }
    }

    return state
}
export default reducer;

在上面的代码中,我们reducer通过添加第三个条件语句来更新我们的函数,该条件语句的类型ERRORerror属性被添加到我们的initialState对象中。

让我们ERRORApp组件中dispatch action类型。

代码语言:javascript复制
// App.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import './App.css'

class App extends Component {


  handleNameChange = (e) => {
    this.props.onChangeName(e.target.value)
  }

  handleClick = () => {

    if (this.props.name) {
      this.props.onAddName()
    } else {
      this.props.onError("Name field cannot be empty")
    }

  }

  render() {
    return (
      <div className="App">

        <div>
          <input type="text"
            placeholder="Name"
            value={this.props.name} onChange={this.handleNameChange} />
          <button onClick={this.handleClick}>Add name</button>

          <p className={this.props.error ? "error active" : "error"}>
          {this.props.error}</p>
          <ul>
            {this.props.allNames && this.props.allNames.map(name => (
              <li key={name}> {name}</li>
            ))}
          </ul></div>

      </div>
    );
  }
}


const mapStatetoProps = (state) => {
  return {
    name: state.name,
    error: state.error,
    allNames: state.allNames
  }
}


const mapDispatchtoProps = (dispatch) => {
  return {
    onChangeName: (name) => dispatch({ type: "CHANGE_NAME", name: name }),
    onAddName: () => dispatch({ type: "ADDNAME" }),
    onError: (err) => dispatch({ type: "ERROR", error: err })
  }
}

export default connect(mapStatetoProps, mapDispatchtoProps)(App);

在上面的代码中,我们在handleClick方法内部添加了条件检查,以便每当用户尝试单击Add name按钮而不输入名称时,我们都会 通过传递错误消息来调用this.props.onError方法。

重构代码

很难在许多地方手动键入操作类型,因此我们要创建两个新文件,分别是actionCreators.jsactionTypes.js

actionTypes.js文件中,我们正在定义所有动作类型。

目前,我们的应用程序中包含三种类型的操作CHANGE_NAMEADDNAME以及ERROR

actionssrc目录中创建一个文件夹。

actions文件夹内创建一个actionTypes.js文件和以下代码。

代码语言:javascript复制
// actionTypes.js
export const ADDNAME = "ADDNAME";
export const CHANGE_NAME = "CHANGE_NAME";
export const ERROR = "ERROR";

actionCreators.jsactions文件夹中创建一个新文件。

代码语言:javascript复制
// actionCreators.js
import { CHANGE_NAME, ADDNAME, ERROR } from './actionTypes'


export function changeName(name) {
    return {
        type: CHANGE_NAME,
        name: name
    }
}

export function addname() {
    return {
        type: ADDNAME
    }
}
export function error(msg) {
    return {
        type: ERROR,
        error: msg
    }
}

在上面,我们创建了三个action创建器,它们返回三种不同类型的动作。

Action创建者是JavaScript函数,它们以一种action类型返回对象。

使用更新您的reducer.js文件actionTypes

代码语言:javascript复制
// reducer.js
import { CHANGE_NAME, ADDNAME, ERROR } from '../actions/actionTypes'

const intialState = { name: "reactgo", allNames: [], error: "" }

const reducer = (state = intialState, action) => {

    if (action.type === ADDNAME) {
        return {
            allNames: state.allNames.concat(state.name),
            name: ""
        }
    }

    if (action.type === CHANGE_NAME) {
        return {
            ...state,
            name: action.name
        }
    }

    if (action.type === ERROR) {
        return {
            ...state,
            error: action.error
        }
    }

    return state


}
export default reducer;

App.js使用actionCreate()更新文件。

代码语言:javascript复制
// App.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { addname, error, changeName } from './actions/actionCreators'
import './App.css'

class App extends Component {


  handleNameChange = (e) => {
    this.props.onChangeName(e.target.value)
  }

  handleClick = () => {

    if (this.props.name) {
      this.props.onAddName()
    } else {
      this.props.onError("Name field cannot be empty")
    }

  }

  render() {
    return (
      <div className="App">

        <div>
          <input type="text"
            placeholder="Name"
            value={this.props.name} onChange={this.handleNameChange} />
          <button onClick={this.handleClick}>Add name</button>
          <p className={this.props.error ? "error active" : "error"}>
          {this.props.error}</p>
          <ul>
            {this.props.allNames && this.props.allNames.map(name => (
              <li key={name}> {name}</li>
            ))}
          </ul></div>

      </div>
    );
  }
}


const mapStatetoProps = (state) => {
  return {
    name: state.name,
    error: state.error,
    allNames: state.allNames
  }
}


const mapDispatchtoProps = (dispatch) => {
  return {
    onChangeName: (name) => dispatch(changeName(name)),
    onAddName: () => dispatch(addname()),
    onError: (err) => dispatch(error(err))
  }
}

export default connect(mapStatetoProps, mapDispatchtoProps)(App);

0 人点赞