Author: Idle fish technology – sea pig

Fish_redux is an open source application development framework for FLUTTER created by the Idle Fish Technology team to solve the problem of high cohesion and low coupling between components within a page. Open source: github.com/alibaba/fis…

From the react_redux

Redux is a familiar framework for those of you on the front end, and Fish_Redux borrows the idea of redux single data flow. When you talk about redux on flutter, your first reaction might be to think of react_redux on React. There is an important concept in react_redux called connect,

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

Simply put, connect allows the user to retrieve data from the Redux store and bind it to the props of the component, which can dispatch an action to modify the data.

So what does the connector in Fish_redux do? Why does connector solve the problem of component cohesion? What should we make of its design?

connector in fish_redux

Fish_redux differs from react_redux at the level of abstraction, although both serve the purpose of joining.

Fish_redux itself is an application framework on Flutter that builds its own component architecture to address high cohesion within components and low coupling between components. From the connector’s perspective, how to solve the cohesion problem is an important consideration in the design.

Fish_redux creates its own Component tree, which aggregates state and dispatch. The state of each child Component is filtered by connector from the parent Component’s state. As shown in the figure:

As you can see, the fish_redux connector’s main function is to associate parent and child Components. The most important operation is the filter. State from top to bottom is a strict tree structure that reuses the Tree structure of Component. Like a data funnel, manage the separation and assembly of data. It shows how to assemble a Component.

For react_redux, the main function is to bind the React framework to Redux, focusing on how to make the React Component redux.

As can be seen from the figure, react_redux and React are parallel structures. The state after mapStateToProps does not have a strict tree structure, that is, for a React component, Its state comes from the Redux Store, not the parent Component’s state. From a framework design perspective, the most important operation for react_redux is attach.

Source code analysis

With that in mind, let’s take a look at how connector in Fish_redux works from a source code perspective, using fish_redux as an example.

class ToDoListPage extends Page<PageState, Map<String, dynamic>> {ToDoListPage(): super(... dependencies: Dependencies<PageState>(adapter: ToDoListAdapter(),slots: <String, Dependent<PageState>>{'report': ReportConnector() + ReportComponent()}),...) ; }Copy the code

In the ToDoListPage constructor, we pass a Dependencies object to the parent constructor. When we construct Dependencies, the parameter slots contains an item named “report”. Is generated by a ReportConnector+ ReportComponent.

From this we draw a simple but very important conclusion:

In fish_redux, a Dependent = connector + Component.

The Dependent represents a unit in page assembly, which can be either a Component(generated by the buildComponent function) or an Adapter(generated by the buildAdapter function). The advantage of this design is that Dependent consolifies the API for View assembly operations without requiring more detail.

Based on our conclusion above, the Connector is used to link a smaller Component unit to a larger Component or Adapter. This is consistent with our previous description.

What exactly is a connector?

Now that you know what connector does, let’s take a look at what it links to and how.

Let’s look at the ReportConnector class definition:

class ReportConnector extends ConnOp<PageState, ReportState>
Copy the code

The ReportConnector descends from ConnOp. All connector operations, including **+** operations, come from ConnOp.

set/get

Since it’s a data pipeline, there will be fetch and drop.

The input to the set function is a good representation of T and P, that is, to merge a subState of type P into a state of type T.

It is easy to understand when looking back at the get function. The get function expresses how to obtain the subState of type P from the state of type T for Dependent to use.

operator +

The overloading of the + operator is where we first see connector in action, and where connector comes into play.

Logic is the parent class of Component and Adapter. It represents the logical layer of page assembly elements, including reducer, Effect, higherEffect and other logic-related elements and assembly process.

Operator + calls createDependent, which then calls the constructor of the _Dependent class, which places logic and connector inside _Dependent. Later during fish_redux’s Component assembly, connector will come into play with external calls to functions in _Dependent.

Connector officially launched

With so much groundwork laid, it’s time for the Connector to come into play.

get

We use Component as an example. We call the _Dependent buildComponent function:

Widget buildComponent(MixedStore<Object> store, Get<T> getter) {final AbstractComponent<P> component = logic; return component.buildComponent(store, () => connector.get(getter())); }Copy the code

The logic here is actually a Component object. When we call Component’s buildComponent function, we use the get function to get the data set required by the current Component from a large parent state. Next, the transformed child state will be used in functions such as ViewBuilder or Redcuer.

This is the connector’s role in data retrieval.

set

Again in the _Dependent class, look at the createSubReducer function:

SubReducer<T> createSubReducer() {final Reducer<P> reducer = logic.reducer; return reducer ! = null ? connector.subReducer(reducer) : null; }Copy the code

We first get the reducer from the external Settings from a Logic(in this case, a Component) object, and then call subReducer to return a subReducer object. The SubReducer is a Reducer after the wrap.

In MutableConn, ConnOp inherits the MutableConn class and also acquires this capability.

SubReducer<T> subReducer(Reducer<P> reducer) {return (T state, Action action, bool isStateCopied) {final P props = get(state); if (props == null) {return state; }final P newProps = reducer(props, action); final bool hasChanged = newProps ! = props; final T copy = (hasChanged && ! isStateCopied)? _clone<T>(state) : state; if (hasChanged) {set(copy, newProps); }return copy; }; }Copy the code

It first obtains a transformed data set props through the GET function, and then calls the original reducer function for logical processing. There is an optimization that also serves as a SubReducer. If the data set changes after the reducer process, If state has already been copied (isStateCopied==true), update the newProps function to state directly via set. (This optimization prevents the parent state from being copied multiple times when multiple child states change).

At this point, connector’s role in data updating has been demonstrated.

ReportConnector

Finally, it’s easy to understand the ReportConnector implementation:

class ReportConnector extends ConnOp<PageState, ReportState> {@overrideReportState get(PageState state) {final ReportState reportState = ReportState(); reportState.total = state.toDos.length; reportState.done =state.toDos.where((ToDoState tds) => tds.isDone).toList().length; return reportState; }@overridevoid set(PageState state, ReportState subState) {}}Copy the code

Obviously, in the get function, ReportState gets the Total /done field from PageState.

conclusion

Fish_redux has been fully refactored into the details page of the Xianyu client. The highly cohesive Component+connector form makes Component reusable and supports five types of details pages. In the future, we will build on fish_redux’s extensive capabilities to create more components to meet the needs of different businesses for the framework.