Praktyczny przykład - XOR

Chcemy zasymulować na ekranie działanie bramek logicznych - na przykładzie XOR (na wyjściu daje 1 gdy tyko na jdnym wejściu mamy 1).

Przygotujmy komponenty (na bazie button) wejścia (ButtonI) i wyjścia (ButtonO):

import React, { Component } from 'react';
import './buttons.css';

class ButtonI extends Component {

  zmiana = () => {
    let nValue='o';
    if (this.props.v==='o') nValue='i';
    this.props.zmiana(this.props.i,nValue);
  }

  render() {
    return (
      <button onClick={this.zmiana}>
        {this.props.v}
      </button>
    );
  }
}

let ButtonO = (props) => (<button>{props.result}</button>);

export default ButtonI;
export {ButtonO};

Zakładamy, że funkcję "zmiana" obsługi kliknięcia otrzymamy od przodka. Wlasność v (value) = stan przycisku wejścia.

Style (button.css) ograniczmy do wielkości przycisków:

button {
  width: 4em;
  height: 4em;
}

W aplikacji (obiekt App) zapamiętajmy stan i1 i i2 (stan wejścia - obu przycisków).

import React, { Component } from 'react';
import ButtonI, { ButtonO } from './buttons';

class App extends Component {

  constructor(props) {
    super(props);
    this.state = {i1 : '0', i2: '1'}
    this.powrot=this.powrot.bind(this);
  }

  powrot(i,v) {
    let i1, i2; 
    if (i===1) { i1=v; i2=this.state.i2}
    else { i2=v;i1=this.state.i1;}
    this.setState({i1 : i1, i2: i2});
  }

  render() {
    return (
      <div className="App">
[<ButtonI zmiana={this.powrot} i={1} v={this.state.i1}/>xor
 <ButtonI zmiana={this.powrot} i={2} v={this.state.i2}/>] =&gt; 
 <ButtonO 
   result={this.state.i1 !== this.state.i2 ? '1' : '0'} />
      </div>
    );
  }
}

export default App;

Zobacz przykład: [023]

Spróbujmy teraz nasz przykład zmienić z użyciem store Redux'a.

Stwórzmy store (store.js):

import { createStore } from 'redux';

const ACTION_ZMIANA = 'ACTION_ZMIANA';

export function zmiana(index,value) {
   return {
     type : ACTION_ZMIANA,
     index,
     value
   }
}

function stanPoczatkowy() {
  return ({
    i1 : '0',
    i2: '0'})
}

const reducer = (state, action) => {
 if (action.type==ACTION_ZMIANA) {
   let i1, i2;
   if (action.index===1) {
       i1=action.value; i2=state.i2
     }
   else { i2=action.value;i1=state.i1;}
   return({
     ...state,
      i1 : i1, i2: i2
    });
 } else {
   return state;
 }
}

export const store = createStore(
  reducer,
  stanPoczatkowy()
)

W stosunku do poprzednich przykładów z Redux mamy tu jedną niewielką komplikację - funkcja kreatora akcji ma dwa parametry: index (numer przycisku) i value (aktualna wartość). Widzimy w jaki sposób są przekazywane w akcjach takie paramtry - są po prostu dopisywane do struktury opisującej zdarzenie. W samym reduktorze można zauważyć użycie operatora spread (...state).

A oto jak uprości się nasz App.js:

import React, { Component } from 'react';
import ButtonI, { ButtonO } from './buttons';
import {connect} from 'react-redux';
import { bindActionCreators } from 'redux';
import { zmiana } from './store';

class App extends Component {

  constructor(props) {
    super(props);
    this.powrot=this.powrot.bind(this);
  }

  powrot(i,v) {
    this.props.zmiana(i,v);
  }


  render() {
    return (
      <div className="App">
       [<ButtonI zmiana={this.powrot} i={1}
                 v={this.props.store.i1}/>xor
        <ButtonI zmiana={this.powrot} i={2}
                 v={this.props.store.i2}/>
       ] =&gt;
       <ButtonO result={this.props.store.i1 !==
            this.props.store.i2 ? '1' : '0'} />
      </div>
      );
    }
  }

///////////// wstrzykiwanie store do props
  const mapStateToProps = state => {
    return {
      store : state
    }
  }

  const mapDispatchToProps = dispatch =>
  bindActionCreators(
    { zmiana },
    dispatch
  )

  export default connect(
    mapStateToProps,
    mapDispatchToProps
  )(App);

Jak widzimy - jeśli pominąć końcowy fragment (kopiuj & wklej) - moduł uległ uproszczeniu - App nie ma w ogóle własnego stanu.

Jeszcze mała zmiana w ReactDOM.render ....

import { store } from './store.js'
import { Provider } from 'react-redux'

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

... i możemy się cieszć działającą aplikacją - zob. przykład [024]