3.3 A simple Switch / Namespaced State
Next, let's add a simple switch function to our RandomGif
component. It's not part of required functionalities of our RandomGif
component. However, implemnt this simple function will help us to understand the some basics that fractal-component
brought to the component state management area.
Firstly, we want to modify src/RandomGif/index.js
to add a switch
button & initialise component state:
import React from "react";
import { ComponentManager } from "fractal-component";
class RandomGif extends React.Component {
constructor(props) {
super(props);
// --- Component state need to be initialised
// --- before `registerComponent` method call
this.state = {
isSwitchOn: false
};
this.componentManager = new ComponentManager(this, {
namespace: "io.github.t83714/RandomGif"
});
}
render() {
return (
<button onClick={() => {}}>
{this.state.isSwitchOn ? "Switch ON" : "Switch OFF"}
</button>
);
}
}
export default RandomGif;
To make the button clickable and display correct status (Switch ON
or Switch OFF
), we just need to mutate component state this.state
and make sure this.state.isSwitchOn
has been set correct value (true
or false
) when the button is clicked.
In a React Component, to achieve that, we usually can mutate this.state
by calling this.setState
method. However, for a ComponentManager
managed component, we cannot do that because this.state
is actually managed by the ComponentManager
and it's part of global application Redux state store. In fact, if you try to call this.setState
in a managed component, you will get an Error:
Error: This component is managed by
fractal-component
. You should dispatch action to mutate component state.
i.e. If you want to mutate the component state this.state
, you should trigger an action and mutate the component state in a namespaced
component reducer. You can provide this reducer via ManageableComponentOptions / reducer method.
Next, you need to define the Action before we can dispatch the action in onClick
event handler of our switch button. To do so, we need to create a file src/RandomGif/actions/types.js
with the Action Type
definition:
export const CLICK = Symbol("CLICK");
Here, fractal-component
's action dispatch system requires all action types are symbols. A Redux App usually won't define Action Type
as symbols
as they are not serialisable and you will need to serialise Actions
when you try to implement Time Travel alike features in your app. However, fractal-component
has sovled this by providing an Action
serialisation / de-serialisation solution via its ActionRegistry
. See AppContainer.serialiseAction & AppContainer.deserialiseAction for more info.
Next, create a file src/RandomGif/actions/index.js
with all action creator function definitions for your component:
import * as actionTypes from "./types";
export function click() {
return {
type: actionTypes.CLICK
};
}
Then, we will need to create our reducer
in src/RandomGif/reducers/index.js
:
import * as actionTypes from "../actions/types";
const reducer = function(state, action) {
switch (action.type) {
case actionTypes.CLICK:
return {
...state,
isSwitchOn: state.isSwitchOn ? false : true
};
default:
return state;
}
};
export default reducer;
You probably have noticed that the reducer is a namespaced
reducer. i.e.
- It only receives the actions that the component receives.
- It only sees the component state
this.state
.
We now can complete our switch button
feature by importing everything above into src/RandomGif/index.js
:
import React from "react";
import { ComponentManager } from "fractal-component";
import * as actionTypes from "./actions/types";
import reducer from "./reducers";
import { click } from "./actions";
class RandomGif extends React.Component {
constructor(props) {
super(props);
this.state = {
isSwitchOn: false
};
this.componentManager = new ComponentManager(this, {
namespace: "io.github.t83714/RandomGif",
// --- register all action types so that actions are serialisable
actionTypes,
reducer
});
}
render() {
return (
<button onClick={() => {
this.componentManager.dispatch(click());
}}>
{this.state.isSwitchOn ? "Switch ON" : "Switch OFF"}
</button>
);
}
}
export default RandomGif;
Please note: In order to make your component actions serialisable, you need to register all your component action types via ManageableComponentOptions.actionTypes
If you run the app via npm start
, you will find the button text will reflects the switch status
along your clicks. You can open the Redux Devtool to observe the component state changes.