如何使用React-Redux模拟事件?

时间:2016-12-13 22:08:28

标签: javascript reactjs events redux store

我正在使用React和Electron构建桌面应用。 由于它的发展很快,我意识到我需要像Redux这样的某种状态管理来避免在组件之间传递许多属性。 我开始阅读Redux官方documentation,但在我的案例中仍然无法弄清楚如何实现它。我被困了!

例如,我有一个主App组件,可以呈现许多子组件。其中一个有一个按钮。点击后,它应该发送"事件"到商店,所以主App可以采取行动。我怎么能做到这一点? 我无法找到事件的概念,而且我已经开始讨论如何开始使用Redux。

为什么要举办活动?因为在这种情况下发送动作并修改应用程序状态对我来说似乎很愚蠢。我只想通知根组件根据用户操作调度操作。 用户与表示组件交互,该组件应该告诉容器组件进行API调用或开始捕获音频/摄像机。

就目前我所知,实现这一目标的唯一方法是改变状态,以便另一个侦听更改的组件检测到一个特殊值,这意味着"嘿,让我们这样做",然后再次改变状态说'嘿,我正在做这个",当它完成时状态再次改变"嘿,它完成了"

有人能指出我正确的方向吗?

3 个答案:

答案 0 :(得分:5)

  

用户与演示组件交互,该演示组件应该告诉容器组件进行API调用或开始捕获音频/摄像机。

也许你的容器组件正在做的比它应该做的更多。考虑React组件不超过两件事情的情况:

  1. 根据道具
  2. 显示DOM元素
  3. 处理用户输入(调度事件)
  4. 如果您没有使用redux并且想在单击按钮时进行API调用,则可能类似于:

    class App extends Component {
      state = { data: {} }
    
      makeAPICall() {
        fetch(url).then(data => this.setState({ data }))
      }
    
      render() {
        <Child 
          data={this.state.data} 
          makeAPICall={this.makeAPICall} 
        />
      }
    }
    
    let Child = ({ data, makeAPICall }) => (
      <button onClick={makeAPICall}>Call API!</button>
    )
    

    App组件负责存储全局状态和处理事件,但是我们必须通过组件树传递该状态和App处理程序,很可能通过永远不会自己使用那些组件的组件道具。

    通过添加Redux,您的应用程序现在可以更好地处理API调用或打开相机等副作用。中间件!

    让这个(糟糕的)插图帮助你:

    enter image description here

    所以现在,您的App组件可以像其他所有组件一样只是一个正常的表示组件,只需根据商店道具显示数据,并在需要时处理任何用户输入/调度操作。让我们使用thunk中间件

    更新上面的示例
    // actions.js
    export let makeAPICall = () => {
      return dispatch => {
        fetch(url).then(data => dispatch({ 
          type: 'API_SUCCESS',
          payload: data,
        })).catch(error => dispatch({ type: 'API_FAIL', payload: error }))
      }
    }
    
    // Child.js
    import { connect } from 'react-redux'
    import { makeAPICall } from './actions'
    
    let Child = ({ dispatch }) => (
      <button onClick={() => dispatch(makeAPICall())}>Call API!</button>
    )
    
    export default connect()(Child)
    

    以这种方式思考React应用程序是非常强大的。关注点的分离非常明确。组件显示内容和处理事件。中间件会处理任何副作用(如果需要的话),而商店只是一个对象,如果数据发生变化,它将导致React重新渲染。

    更新:&#34;模态问题&#34;

    React应用程序可能包含一些全局内容,如模态和工具提示。不要考虑&#34;开放式模式&#34;事件..想想&#34;当前的模态内容是什么?&#34;。

    模态设置可能看起来像这样:

    // modalReducer.js
    function reducer (state = null, action) {
       if (action.type === 'UPDATE_MODAL') {
         return action.payload
       } 
    
       // return default state
       return state
    }
    
    // App.js
    let App = connect(state => ({ modal: state.modal }))(
      props => 
        <div>
           <OtherStuff />
           <Modal component={props.modal} />
        </div>
    )
    
    // Modal.js
    let Modal = props =>
      <div
        style={{ 
          position: 'fixed',
          width: '100vw', height: '100vh',
          opacity: props.component ? 1 : 0,
        }}
      >
        {props.component}
      </div>
    
    // Child.js
    
    let Child = connect()(props =>
      <button onClick={e =>
        dispatch({ 
          type: 'UPDATE_MODAL'
          payload: <YourAwesomeModal />
        })
      }>
        Open your awesome modal!
      </button>
    )
    

    这只是一个例子,但效果会很好!当state.modalnull时,您的模态具有0不透明度且无法显示。当您发送UPDATE_MODAL并传入一个组件时,模态将显示您发送的任何内容,并将不透明度更改为1,以便您可以看到它。稍后您可以发送{ type: 'UPDATE_MODAL', payload: null }来关闭模式。

    希望这能给你一些思考的东西!

    绝对是read this answer by Dan。他的方法类似,但存储模态&#34;元数据&#34;与组件本身相比,它更适合Redux的时尚旅行等。

答案 1 :(得分:0)

您认为它看起来很愚蠢的原因是因为您不希望您的表示组件能够降低感知能力吗?如果是这样mapDispatchToPropsbindActionCreators可能有助于整理,例如:

// App.js
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { someAction } from './actions';
import Button from './Button';

const App = ({ onButtonClick }) => (
    <div>
        Hello.
        <Button onClick={onButtonClick}>Click me.</Button>
    </div>
);

export default connect(null, dispatch => {
    return bindActionCreators({
        onButtonClick: someAction
    }, dispatch);
})(App);

// Button.js
import React from 'react';

export default Button = ({ onClick, children }) => <button onClick={onClick}>{children}</button>;

正如您所看到的,只有连接的容器组件知道该操作,Button(甚至是App)都不知道click会触发操作。

答案 2 :(得分:0)

对于它的价值,我遇到了类似的问题(单击树中其他位置的按钮并导致地图重置其视口),并使用简单的增量键解决了该问题。

按钮调度动作:

export const RESET_MAP = "RESET_MAP";
export const resetMap = () => {
  return {
    type: RESET_MAP,
  };
};

在减速器中:

case RESET_MAP:
  return Object.assign({}, state, {
    setvar: state.setvar + 1
  });

在地图上:

static getDerivedStateFromProps(newProps, state) {
  var newState = null;

  if (newProps.setvar !== state.setvar) {
      newState = {
        setvar: newProps.setvar,
        [other magic to reset the viewport]
      }
  }

  return newState;