亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

Table of Contents
Familiar with MST models
Let's create a React chart component
Create our memory
Define the properties of the model
Define the operation
Define the view
Home Web Front-end CSS Tutorial Making a Chart? Try Using Mobx State Tree to Power the Data

Making a Chart? Try Using Mobx State Tree to Power the Data

Apr 15, 2025 am 09:49 AM

Making a Chart? Try Using Mobx State Tree to Power the Data

Who likes charts? Everyone likes it, right? There are many ways to create charts, including many libraries. For example, D3.js, Chart.js, amCharts, Highcharts and Chartist, which are just a few of the many options.

But we don't necessarily need a chart library to create charts. Mobx-state-tree (MST) is an intuitive alternative to Redux to manage state in React. We can build interactive custom charts using simple SVG elements and use MST to manage and manipulate the data of the chart. If you have tried building charts with tools like D3.js in the past, I think you'll find this approach more intuitive. Even if you are an experienced D3.js developer, I still think you will be interested in the power of MST as a visual data architecture.

Here is an example of MST using to drive charts:

This example uses the scale function of D3, but the chart itself is rendered using the SVG element in JSX. I don't know of any chart library with the option to flash hamster points, so this is a great example of why building your own chart is great – and it's not as difficult as you think!

I've been building charts with D3 for over 10 years and while I love the power of it, I always find that my code ends up being clumsy and hard to maintain, especially when dealing with complex visualizations. MST completely changes all this by providing an elegant way to separate data processing from rendering. I hope this article encourages you to give it a try.

Familiar with MST models

First, let's give a quick overview of the appearance of the MST model. This is not an in-depth tutorial on everything about MST. I just want to show the basics because in reality you only need that basics 90% of the time.

Here is a Sandbox with code for a simple to-do list built using MST. A quick look and I'll explain what each section does.

First, the shape of the object is defined by the typed definition of the model attributes. Simply put, this means that instances of the to-do model must have a title, which must be a string, and by default the "done" property is false.

 <code>.model("Todo", { title: types.string, done: false //這相當(dāng)于types.boolean,默認(rèn)為false })</code>

Next, we have the views and manipulation functions. A view function is a method of accessing calculated values ??based on data in the model without changing the data saved by the model. You can think of them as read-only functions.

 <code>.views(self => ({ outstandingTodoCount() { return self.todos.length - self.todos.filter(t => t.done).length; } }))</code>

On the other hand, operation functions allow us to safely update data. This is always done in the background in a non-mutable way.

 <code>.actions(self => ({ addTodo(title) { self.todos.push({ id: Math.random(), title }); } }));</code>

Finally, we create a new storage instance:

 <code>const todoStore = TodoStore.create({ todos: [ { title: "foo", done: false } ] });</code>

To show what the memory actually does, I added some console logs to show the output of outStandingTodoCount() before and after the toggle function that triggers the first instance of Todo.

 <code>console.log(todoStore.outstandingTodoCount()); // 輸出:1 todoStore.todos[0].toggle(); console.log(todoStore.outstandingTodoCount()); // 輸出:0</code>

As you can see, MST provides us with a data structure that allows us to easily access and manipulate data. More importantly, its structure is very intuitive and the code is clear at a glance - there is no reducer!

Let's create a React chart component

OK, now that we have understood what MST looks like, let's use it to create a memory that manages the data of the chart. However, we will start with chart JSX, because once we know what data is needed, it is much easier to build memory.

Let's take a look at JSX that renders the chart.

The first thing to note is that we are using styled-components to organize our CSS. If you are not familiar with it, Cliff Hall has a great article showing its use in React applications.

First, we are rendering the drop-down menu that will change the axes of the chart. This is a fairly simple HTML drop-down menu wrapped in a styled component. It should be noted that this is a controlled input whose state is set using the selectedAxes value in our model (we will cover this later).

 <code>model.setSelectedAxes(parseInt(e.target.value, 10)) } defaultValue={model.selectedAxes} ></code>

Next is the chart itself. I've split the axes and points into their own components, which are in separate files. By keeping each file concise, this really helps keep the code maintainable. Furthermore, this means that if we want to use line charts instead of points, we can reuse the axes. This is very effective when dealing with large projects with multiple chart types. It also makes it easy to test components individually, whether it is programmatically or manually tested in the Active Style Guide.

 <code>{model.ready ? (</code>
    <code>{model.ready ? (</code> <axes xlabel="{xAxisLabels[model.selectedAxes]}" xticks="{model.getXAxis()}" ylabel="{yAxisLabels[model.selectedAxes]}" yticks="{model.getYAxis()}"></axes><points points="{model.getPoints()}"></points>
) : (
  <loading></loading>
)}

Try commenting out the axes and points components in the Sandbox above to see how they work independently.

Finally, we wrap the component in an observer function. This means that any changes in the model will trigger re-rendering.

 <code>export default observer(HeartrateChart);</code>

Let's take a look at the Axes component:

As you can see, we have an XAxis and a YAxis. Each has a label and a set of tick marks. We'll cover how to create markers later, but here you should note that each axis consists of a set of ticks generated by traversing a set of objects with labels and x or y values ??(depending on which axis we're rendering).

Try changing some attribute values ??of the element to see what happens...or what breaks! For example, change the line element in YAxis to the following:

<code><line x1="{30}" x2="95%" y1="{0}" y2="{y}"></line></code>

The best way to learn how to build visualizations using SVG is to simply experiment and destroy things. ?

OK, that's half of the chart. Now let's look at the Points component.

Each point on the chart consists of two parts: an SVG image and a circular element. The image is an animal icon, and the circle provides an animation of impulsiveness that is visible when hovering the mouse over the icon.

Try commenting out the image element, then commenting out the circular element and see what happens.

This time the model must provide an array of point objects that provide us with four properties: the x and y values ??for the anchor points on the chart, the label of the points (the name of the animal), and the pulse, which is the duration of the pulse animation for each animal icon. Hopefully this all looks intuitive and logical.

Again, try modifying the property value to see which changes and interrupts are made. You can try setting the y property of the image to 0. Trust me, this is much easier than reading the W3C specification for SVG image elements!

Hope this gives you an idea of ??how we render charts in React. Now, you just need to create a model with appropriate operations to generate the data we need to loop through in JSX.

Create our memory

Here is the complete code for the memory:

I broke the code into the three parts mentioned above:

  1. Define the properties of the model
  2. Define the operation
  3. Define the view

Define the properties of the model

Everything we define here is accessible from external properties as a model instance, and – if observable wrapper components are used – any changes to these properties will trigger re-rendering.

 <code>.model('ChartModel', { animals: types.array(AnimalModel), paddingAndMargins: types.frozen({ paddingX: 30, paddingRight: 0, marginX: 30, marginY: 30, marginTop: 30, chartHeight: 500 }), ready: false, // 表示types.boolean,默認(rèn)為false selectedAxes: 0 // 表示types.number,默認(rèn)為0 })</code>

Each animal has four data points: name (Creature), lifespan (Longevity__Years_), weight (Mass__grams_), and resting heart rate (Resting_Heart_Rate__BPM_).

 <code>const AnimalModel = types.model('AnimalModel', { Creature: types.string, Longevity__Years_: types.number, Mass__grams_: types.number, Resting_Heart_Rate__BPM_: types.number });</code>

Define the operation

We only have two operations. The first (setSelectedAxes) is called when changing the drop-down menu, which updates the selectedAxes property, which in turn determines which data is used to render the axis.

 <code>setSelectedAxes(val) { self.selectedAxes = val; },</code>

The setUpScales operation requires more explanation. This function is called in the useEffect hook function immediately after the chart component is mounted, or after the window is resized. It accepts an object containing the width of the DOM containing the element. This allows us to set a scale function for each axis to fill the full available width. I will explain the scale function soon.

To set the scale function we need to calculate the maximum values ??for each data type, so we first traverse the animals to calculate these maximum values ??and minimum values. We can use zeros as the minimum value of any scale we want to start from zero.

 <code>// ... self.animals.forEach( ({ Creature, Longevity__Years_, Mass__grams_, Resting_Heart_Rate__BPM_, ...rest }) => { maxHeartrate = Math.max( maxHeartrate, parseInt(Resting_Heart_Rate__BPM_, 10) ); maxLongevity = Math.max( maxLongevity, parseInt(Longevity__Years_, 10) ); maxWeight = Math.max(maxWeight, parseInt(Mass__grams_, 10)); minWeight = minWeight === 0 ? parseInt(Mass__grams_, 10) : Math.min(minWeight, parseInt(Mass__grams_, 10)); } ); // ...</code>

Now set the scale function! Here we will use the scaleLinear and scaleLog functions of D3.js. When setting these functions we specify the domain, which is the minimum and maximum input that the function can expect, and the range, which is the maximum and minimum output.

For example, when I call self.heartScaleY with the maxHeartate value, the output will equal marginTop. This makes sense because this will be at the top of the chart. For the lifetime attribute, we need to have two scale functions, because this data will be displayed on the x-axis or y-axis, depending on which drop-down option is selected.

 <code>self.heartScaleY = scaleLinear() .domain([maxHeartrate, minHeartrate]) .range([marginTop, chartHeight - marginY - marginTop]); self.longevityScaleX = scaleLinear() .domain([minLongevity, maxLongevity]) .range([paddingX marginY, width - marginX - paddingX - paddingRight]); self.longevityScaleY = scaleLinear() .domain([maxLongevity, minLongevity]) .range([marginTop, chartHeight - marginY - marginTop]); self.weightScaleX = scaleLog() .base(2) .domain([minWeight, maxWeight]) .range([paddingX marginY, width - marginX - paddingX - paddingRight]);</code>

Finally, we set self.ready to true because the chart is ready for rendering.

Define the view

We have two sets of view functions. The first set outputs the data required to render the axis scale (I said we'll get there!) the second set outputs the data required to render the point. We will first look at the scale function.

There are only two tick functions called from the React application: getXAxis and getYAxis. These functions simply return the output of other view functions based on the value of self.selectedAxes.

 <code>getXAxis() { switch (self.selectedAxes) { case 0: return self.longevityXAxis; break; case 1: case 2: return self.weightXAxis; break; } }, getYAxis() { switch (self.selectedAxes) { case 0: case 1: return self.heartYAxis; break; case 2: return self.longevityYAxis; break; } },</code>

If we look at the Axis functions themselves, we can see that they use the ticks method of the scale function. This returns a set of numbers that fit the axis. We then iterate through these values ??to return the data required by the axis component.

 <code>heartYAxis() { return self.heartScaleY.ticks(10).map(val => ({ label: val, y: self.heartScaleY(val) })); } // ...</code>

Try changing the parameter value of the ticks function to 5 and see how it affects the chart: self.heartScaleY.ticks(5).

Now we have a view function that returns the data required by the Points component.

If we look at longevityHeartratePoints (which returns the point data for the Lifespan vs Heart Rate graph), we can see that we are traversing the animal array and using the appropriate scale function to get the x and y positions of the points. For pulse properties, we use some mathematical methods to convert the number of beats per minute of the heart rate into a numerical value representing the duration of a single heartbeat in milliseconds.

 <code>longevityHeartratePoints() { return self.animals.map( ({ Creature, Longevity__Years_, Resting_Heart_Rate__BPM_ }) => ({ y: self.heartScaleY(Resting_Heart_Rate__BPM_), x: self.longevityScaleX(Longevity__Years_), pulse: Math.round(1000 / (Resting_Heart_Rate__BPM_ / 60)), label: Creature }) ); },</code>

At the end of the store.js file, we need to create a Store model and instantiate it with the raw data of the animal object. It is common practice to attach all models to the parent Store model, and then access these models through the top-level provider as needed.

 <code>const Store = types.model('Store', { chartModel: ChartModel }); const store = Store.create({ chartModel: { animals: data } }); export default store;</code>

That's it! Here is our demonstration:

This is by no means the only way to organize data in JSX to build charts, but I found it very efficient. I've built a custom chart library for large enterprise clients using this structure and stack in a real environment and was shocked by how well MST performed in this purpose. I hope you have the same experience!

The above is the detailed content of Making a Chart? Try Using Mobx State Tree to Power the Data. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undress AI Tool

Undress AI Tool

Undress images for free

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

CSS tutorial for creating loading spinners and animations CSS tutorial for creating loading spinners and animations Jul 07, 2025 am 12:07 AM

There are three ways to create a CSS loading rotator: 1. Use the basic rotator of borders to achieve simple animation through HTML and CSS; 2. Use a custom rotator of multiple points to achieve the jump effect through different delay times; 3. Add a rotator in the button and switch classes through JavaScript to display the loading status. Each approach emphasizes the importance of design details such as color, size, accessibility and performance optimization to enhance the user experience.

Addressing CSS Browser Compatibility issues and prefixes Addressing CSS Browser Compatibility issues and prefixes Jul 07, 2025 am 01:44 AM

To deal with CSS browser compatibility and prefix issues, you need to understand the differences in browser support and use vendor prefixes reasonably. 1. Understand common problems such as Flexbox and Grid support, position:sticky invalid, and animation performance is different; 2. Check CanIuse confirmation feature support status; 3. Correctly use -webkit-, -moz-, -ms-, -o- and other manufacturer prefixes; 4. It is recommended to use Autoprefixer to automatically add prefixes; 5. Install PostCSS and configure browserslist to specify the target browser; 6. Automatically handle compatibility during construction; 7. Modernizr detection features can be used for old projects; 8. No need to pursue consistency of all browsers,

What is the difference between display: inline, display: block, and display: inline-block? What is the difference between display: inline, display: block, and display: inline-block? Jul 11, 2025 am 03:25 AM

Themaindifferencesbetweendisplay:inline,block,andinline-blockinHTML/CSSarelayoutbehavior,spaceusage,andstylingcontrol.1.Inlineelementsflowwithtext,don’tstartonnewlines,ignorewidth/height,andonlyapplyhorizontalpadding/margins—idealforinlinetextstyling

Styling visited links differently with CSS Styling visited links differently with CSS Jul 11, 2025 am 03:26 AM

Setting the style of links you have visited can improve the user experience, especially in content-intensive websites to help users navigate better. 1. Use CSS's: visited pseudo-class to define the style of the visited link, such as color changes; 2. Note that the browser only allows modification of some attributes due to privacy restrictions; 3. The color selection should be coordinated with the overall style to avoid abruptness; 4. The mobile terminal may not display this effect, and it is recommended to combine it with other visual prompts such as icon auxiliary logos.

Creating custom shapes with css clip-path Creating custom shapes with css clip-path Jul 09, 2025 am 01:29 AM

Use the clip-path attribute of CSS to crop elements into custom shapes, such as triangles, circular notches, polygons, etc., without relying on pictures or SVGs. Its advantages include: 1. Supports a variety of basic shapes such as circle, ellipse, polygon, etc.; 2. Responsive adjustment and adaptable to mobile terminals; 3. Easy to animation, and can be combined with hover or JavaScript to achieve dynamic effects; 4. It does not affect the layout flow, and only crops the display area. Common usages are such as circular clip-path:circle (50pxatcenter) and triangle clip-path:polygon (50%0%, 100 0%, 0 0%). Notice

What is the CSS Painting API? What is the CSS Painting API? Jul 04, 2025 am 02:16 AM

TheCSSPaintingAPIenablesdynamicimagegenerationinCSSusingJavaScript.1.DeveloperscreateaPaintWorkletclasswithapaint()method.2.TheyregisteritviaregisterPaint().3.ThecustompaintfunctionisthenusedinCSSpropertieslikebackground-image.Thisallowsfordynamicvis

How to create responsive images using CSS? How to create responsive images using CSS? Jul 15, 2025 am 01:10 AM

To create responsive images using CSS, it can be mainly achieved through the following methods: 1. Use max-width:100% and height:auto to allow the image to adapt to the container width while maintaining the proportion; 2. Use HTML's srcset and sizes attributes to intelligently load the image sources adapted to different screens; 3. Use object-fit and object-position to control image cropping and focus display. Together, these methods ensure that the images are presented clearly and beautifully on different devices.

What are common CSS browser inconsistencies? What are common CSS browser inconsistencies? Jul 26, 2025 am 07:04 AM

Different browsers have differences in CSS parsing, resulting in inconsistent display effects, mainly including the default style difference, box model calculation method, Flexbox and Grid layout support level, and inconsistent behavior of certain CSS attributes. 1. The default style processing is inconsistent. The solution is to use CSSReset or Normalize.css to unify the initial style; 2. The box model calculation method of the old version of IE is different. It is recommended to use box-sizing:border-box in a unified manner; 3. Flexbox and Grid perform differently in edge cases or in old versions. More tests and use Autoprefixer; 4. Some CSS attribute behaviors are inconsistent. CanIuse must be consulted and downgraded.

See all articles