如何使用React-redux避免代码中的局部状态?

时间:2019-03-29 05:19:56

标签: reactjs redux react-redux

我正在学习redux,并编写了一个简单的代码,该代码使用store,action和reducer。我正在使用store.subscribe()来监听状态的更改,并使用它来更新我的本地状态。

下面是我在index.js中的全部代码:

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';

var store = createStore(changeState);
var initialState = {
    qty: 0,
    price: 0
}

function changeState(state = initialState, action) {
    switch (action.type) {
        case 'INCREMENT':
            var stateCopy1 = Object.assign({}, state);
            stateCopy1.qty = stateCopy1.qty + action.qty;
            stateCopy1.price = stateCopy1.price + action.price;
            return stateCopy1;
        default:
            return state;

    }
}

class Home extends React.Component {
    render() {
        return (
            <React.Fragment>
                <Comp1 /><br />
                <Comp2 />
            </React.Fragment>
        )
    }
}
class Comp1 extends React.Component {
    increase = () => {
        var action = {
            type: 'INCREMENT',
            qty: 1,
            price: 100
        }
        store.dispatch(action);
    }


    render() {
        return (
            <button type="button" onClick={this.increase}>Increase</button>
        )
    }

}
class Comp2 extends React.Component {
    constructor() {
        super();
        this.state = {
            cartQty: 0,
            cartPrice: 0
        }
    }
    render() {
        store.subscribe(() => {
            var globalState = store.getState();
            this.setState({cartQty:globalState.qty,cartPrice:globalState.price});
        })
        return (
            <div>
                <h1>Total items in cart: {this.state.cartQty}</h1>
                <h1>Total price of cart :{this.state.cartPrice}</h1>
            </div>
        )
    }
}

ReactDOM.render(<Home />, document.getElementById('root'));

我想使用react-redux来避免本地状态和订阅。我了解了connect()和mapStateToProps()和。但是我无法在下面的代码中弄清楚如何使用它们。如何在代码中实现这些部分?

6 个答案:

答案 0 :(得分:4)

当然可以,首先让我们确保概念清楚。

Redux已经具有“状态”,因此将其复制到内部状态是多余的。

connect()
此便捷方法用于将redux的状态映射到组件中的props。也就是说,您没有将状态复制到另一个状态,而是将Redux的状态用作props,它们是不可变的,更像是对Redux内部真实数据的引用。
它是通过模式调用hoc构建的,但这是另一个问题的教训。了解hoc的重要一点是,它将一个组件作为参数,并返回一个新的组件,即改进的组件。

mapStateToProps()
这将是告诉connect您想要进入组件内部redux状态的哪一部分的方法。这种方法可以接收完整的redux状态,提取要使用的属性,然后将其作为props返回到组件。

现在,您缺少这里的关键部分,那就是redux的...
Provider
这部分应该包装您所有的应用程序(或您希望其具有Redux访问权的部分,通常是全部),并且是负责将Redux存储发送到树上的一个程序,因此connect可以稍后再抓取(这是通过react的context实现的,但这又是另一回事了)。
您会像import {Provider} from 'react-redux';这样的提供者,然后将其作为道具提供给商店。...store(明智吗?)

足够的聊天功能,让我们开始工作吧。

我们从导入开始,让我们获得所需的一切:

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import {Provider, connect} from 'react-redux';

我们在Provider组件和connect专案中又添加了两点。

var initialState = {
    qty: 0,
    price: 0
}

function changeState(state = initialState, action) {
    switch (action.type) {
        case 'INCREMENT':
            var stateCopy1 = Object.assign({}, state);
            stateCopy1.qty = stateCopy1.qty + action.qty;
            stateCopy1.price = stateCopy1.price + action.price;
            return stateCopy1;
        default:
            return state;

    }
}
var store = createStore(changeState);

现在,您看到那里发生了什么吗?究竟!没有。您的reducer可以保持原样,我们不会移动,尽管对于大型应用程序,您可能希望学习combine reducers

class Home extends React.Component {
    render() {
        return (
            <Provider store={store}>
                <Comp1 /><br />
                <Comp2 />
            </Provider>
        )
    }
}

好的,您的Fragment不见了,对此感到抱歉,但是现在不再需要了。 Fragment用于返回两个或多个组件,但是由于Provider现在正在包装这些组件,因此无需使用Fragment
关于Provider,您只需要将其放在所有内容之外,然后将其命名为store。很容易。

class Comp1 extends React.Component {
    increase = () => {
        var action = {
            type: 'INCREMENT',
            qty: 1,
            price: 100
        }
        store.dispatch(action);
    }


    render() {
        return (
            <button type="button" onClick={this.increase}>Increase</button>
        )
    }

}

在最后一个组件中,我们什么也没移动。尽管我们应该有,但是请看看您如何直接将store用于dispatch action。在普通应用中,此组件将位于另一个文件中,因此您将无权访问store属性。接下来,我们的朋友connect又来了,它帮助我们通过名为heremapDispatchToProps函数来调度动作,但这又是一天了。

接下来,我们所有人都在等待connect方法:

class Comp2 extends React.Component {

    render() {

        return (
            <div>
                <h1>Total items in cart: {this.props.qty}</h1>
                <h1>Total price of cart :{this.props.price}</h1>
            </div>
        )
    }
}

function mapStateToProps( state ){
    return { qty: state.qty, price:state.price }
}

Comp2 = connect(mapStateToProps)(Comp2);

这可能有点令人困惑,所以让我解释一下:
首先,我们删除了与组件状态有关的所有内容。我们不再使用它,因为那是您想要的吗?而且组件现在更精简了。还有凉爽的
但是后来发生了什么?好吧,首先我们要定义mapStateToProps函数。这会将redux的状态转换为您的组件状态。这次,我们将每个属性从redux的状态发送到您的组件,但是在更大的应用程序中情况并非如此,在更大的应用程序中,redux拥有内部所有内容的状态,这可能是购物车项目,应用程序主题颜色,用户的信息等。很多事情,因此在此函数内,我们仅选择对组件内部感兴趣的属性。
现在连接调用...我们正在重新定义我们的组件,这有点奇怪,但是我会尽力解释。
connect收到了我们的mapStateToProps方法,然后收到了我们的组件。然后他们在connect内配对并生出了另一个组件,这个组件将拥有我们首先定义为孩子的组件,并且始终将其作为道具请求的redux状态的一部分发送给它。

答案 1 :(得分:3)

您需要照顾的事情很少。

首先,您需要安装react-redux,然后在将存储传递到的顶层使用Provider组件

第二:您需要连接需要访问storedispatch

的组件

第三:您需要渲染连接的组件

第四:您需要将mapStateToProps传递到访问状态的容器,并需要mapDispatchToProps来访问调度或使动作创建者可作为组件的道具使用。如果您没有将mapDispatchToProps传递给connect,则默认情况下dispatch支持可用于组件。您可以查看 API docs here

第五在定义changeState reducer和initialState之后创建商店

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { connect, Provider } from 'react-redux'

var initialState = {
    qty: 0,
    price: 0
}

function changeState(state = initialState, action) {
    switch (action.type) {
        case 'INCREMENT':
            var stateCopy1 = Object.assign({}, state);
            stateCopy1.qty = stateCopy1.qty + action.qty;
            stateCopy1.price = stateCopy1.price + action.price;
            return stateCopy1;
        default:
            return state;

    }
}

var store = createStore(changeState);

class Comp1 extends React.Component {
    increase = () => {
        var action = {
            type: 'INCREMENT',
            qty: 1,
            price: 100
        }
        this.props.dispatch(action);
    }


    render() {
        return (
            <button type="button" onClick={this.increase}>Increase</button>
        )
    }

}
const Comp1Container = connect()(Comp1);

class Comp2 extends React.Component {
    render() {

        return (
            <div>
                <h1>Total items in cart: {this.props.cartQty}</h1>
                <h1>Total price of cart :{this.props.cartPrice}</h1>
            </div>
        )
    }
}

const mapStateToProps = (state) => {
   return  {
      cartQty:state.qty,
      cartPrice: state.price
   }
}
const Comp2Container = connect(mapStateToProps)(Comp2);


class Home extends React.Component {
    render() {
        return (
            <React.Fragment>
                <Comp1Container /><br />
                <Comp2Container />
            </React.Fragment>
        )
    }
}
ReactDOM.render(<Provider store={store}><Home /></Provider>, document.getElementById('root'));

Working demo

答案 2 :(得分:0)

react-redux中的使用提供程序,您的商店将可用于所有子组件

                var store = createStore(changeState)
                <Provider store={store}>
                   {child}
                </Provider>

答案 3 :(得分:0)

下面是您需要做的,我举了一个 comp1 组件的示例, 还要检查此链接https://redux.js.org/basics/usage-with-react有助于获取更多信息。

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { connect } from 'react-redux'

class Comp1 extends React.Component {
    increase = () => {
        var action = {
            type: 'INCREMENT',
            qty: 1,
            price: 100
        }
        store.dispatch(action);
    }


    render() {
        return (
            <button type="button" onClick={this.increase}>Increase</button>
        )
    }
}

mapStateToProps = state =>{
  //code to map State to props
}

mapDispatchToProps = dispatch =>{
  //code to map dispatch to props
}

export deafult connect(mapStateToProps,mapDispatchToProps)(Comp1);

答案 4 :(得分:0)

代码

import {combineReducers, createStore} from 'redux';
import {Provider, connect} from 'react-redux'; 
//reducer
var initialState = {
    qty: 0,
    price: 0
}

function cart(state = initialState, action) {
    switch (action.type) {
        case 'INCREMENT':
            var stateCopy1 = Object.assign({}, state);
            stateCopy1.qty = stateCopy1.qty + action.qty;
            stateCopy1.price = stateCopy1.price + action.price;
            return stateCopy1;
        default:
            return state;

    }
}

const rootReducer = combineReducers({cart});

//Actions
const increase = () => {
        var action = {
            type: 'INCREMENT',
            qty: 1,
            price: 100
        }
    }

let Comp1 = (props) => {
    return (
        <button type="button" onClick={props.increase}>Increase</button>
    )
}
Comp1 = connect(null, {increment})(Comp1);

let Comp2 = (props) => {
        return (
            <div>
                <h1>Total items in cart: {props.qty}</h1>
                <h1>Total price of cart :{props.price}</h1>
            </div>
        )
    }
const mapStateToProps = ({state}) => state.cart
Comp2 = connect(mapStateToProps)(Comp2);

//create store
const store = createStore(rootReducer);

class Home extends React.Component {
    render() {
        return (
            <Provider store={store}>
                <Comp1 /><br />
                <Comp2 />
            </Provider >
        )
    }
}

ReactDOM.render(<Home />, document.getElementById('root'));

根据您的示例,有关如何使用redux和react-redux的理论参考。这应该让您了解如何使用两者,并且在Google的帮助下,您可以完成我的示例。

答案 5 :(得分:0)

首先,您希望开发一个在学习过程中更加模块化的项目,对您来说会更容易。

因此,设置一个src/index.js,您已经有一个index.js文件,我要说是您的根index.js,然后像这样重构它:

import React from "react";
import ReactDOM from "react-dom";
import "./index.scss";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";

import App from "./components/App";
import reducers from "./reducers";

const store = createStore(reducers, applyMiddleware(thunk));

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.querySelector("#root")
);

因此Provider在层次结构的顶部,并且已经实现了Provider,您将能够使用connect()函数,否则会出现错误。

在开始实施connect()之前,让我们先移下层次结构,该层次结构在React-Redux中非常重要。

Provider-> App->子组件。

接下来是components/App.js

import React from "react";
import "./App.scss";
import Home from "./Home";

const App = () => {
  return (
    <div className="App">
      <Home />
    </div>
  );
};

export default App;

因此,假设您要将connect()连接到Home组件,我认为您没有指定,但这是这样的:

import React from "react";
import { connect } from "react-redux";
import { fetchLocations } from "../actions";

class Home extends React.Component {
  componentDidMount() {
    this.props.fetchLocations();
  }

  render() {
    return <div>Home List</div>;
  }
}

export default connect(
  null,
  { fetchLocations }
)(Home);

您没有谈论动作创建者或有关数据的详细信息,因此,由于您拥有Home组件,因此我只想收集房屋清单。因此,在上方我将connect()函数导入了组件内部,然后在下方实现了它。请注意,我已将null放置在mapStateToProps的最终位置。同样,您没有详细说明状态,因此,如果我为您设置状态,null足以使您的应用正常运行,直到您准备实施mapStateToProps为止,但是您需要采取一些措施因此我创建了fetchLocations操作,并在connect()内部实现。

对于减速器,您可以执行reducers/locationsReducer.js

export default () => {
  return 123;
};

然后使用您的组合减速器reducers/index.js来实现它:

import { combineReducers } from "redux";
import locationsReducer from "./locationsReducer";

export default combineReducers({
  locations: locationsReducer
});

我相信您现在拥有一个运行正常的React-Redux应用程序,您应该可以从那里拿走它。