如何使用 React Hooks 消费 Spring WebFlux 服务
how use React Hooks to consume Spring WebFlux service
我 understand/believe 我应该支持功能组件而不是 Class 组件,以便应用责任隔离。随着 React Hooks 的出现,它带来了一种新方法来避免 Class 并且仍然具有保持状态的功能。
我创建了一个非常简单的 Web Flux 休息服务。我使用 Hooks 和 Axon 创建了一个非常简单的 React 页面,我得到了 "data.map is not a function at RestApiHooksComponent"。如果我尝试这个使用典型休息服务的完全相同的代码,它将正常工作(我所说的典型休息服务是指没有任何反应功能的同步代码)。
我想我在用 Web Flux 计算 React 时遗漏了一些基本概念。关于如何通过 React 使用 Webflux 的示例很少,但没有一个示例显示如何使用 Hooks(至少我没有找到)。 Hooks 不符合 Reactive 范式吗?基于我对 React 有限的知识,我想这将是一个很好的匹配:常见的用户案例是使用流中的服务列表并在可用时显示它。
前端
package.json
...
"dependencies": {
"axios": "^0.18.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "3.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
...
App.js
import React, { useEffect, useState } from "react";
import axios from "axios";
export default function RestApiHooksComponent() {
const [data, setData] = useState([]);
useEffect(() => {
axios
.get("http://127.0.0.1:8080")
.then(result => setData(result.data));
}, []);
return (
<div>
<ul>
{data.map(item => (
<li key={item.result}>
{item.result}: {item.result}
</li>
))}
</ul>
</div>
);
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
LoanTable.js
import React from 'react'
const LoanTable = props => (
<table>
<thead>
<tr>
<th>Result</th>
</tr>
</thead>
<tbody>
{props.loans.length > 0 ? (
props.loans.map(loan => (
<tr>
<td>{loan.result}</td>
</tr>
))
) : (
<tr>
<td>No loans</td>
</tr>
)}
</tbody>
</table>
)
export default LoanTable
Webflux 休息服务
控制器:
@RestController
public class LoansController {
@Autowired
private LoansService loansService;
@CrossOrigin
@RequestMapping(method = RequestMethod.GET, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
@ResponseBody
public Flux<Loans> findAll() {
Flux<Loans> loans = loansService.findAll();
return loans;
}
}
服务:
@Service
public class LoansService implements ILoansService {
@Autowired
private LoansRepository loansRepository;
@Override
public Flux<Loans> findAll() {
return loansRepository.findAll();
}
}
Webflux 配置
@Configuration
@EnableWebFlux
public class WebFluxConfig implements WebFluxConfigurer
{
}
没有太多相关的细节,但我添加了它以表明它是完全无阻塞代码:
MongoDb 配置
@Configuration
@EnableReactiveMongoRepositories(basePackages = "com.mybank.web.repository")
public class MongoConfig extends AbstractReactiveMongoConfiguration
{
@Value("${port}")
private String port;
@Value("${dbname}")
private String dbName;
@Override
public MongoClient reactiveMongoClient() {
return MongoClients.create();
}
@Override
protected String getDatabaseName() {
return dbName;
}
@Bean
public ReactiveMongoTemplate reactiveMongoTemplate() {
return new ReactiveMongoTemplate(reactiveMongoClient(), getDatabaseName());
}
}
Pom:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mybank</groupId>
<artifactId>web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>web</name>
<description>webflux</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
*** 已编辑
网络通量服务输出
data:{"timestamp":1558126555269,"result":"8"} data:{"timestamp":1558132444247,"result":"10"} data:{"timestamp":1558132477916,"result":"10"} data:{"timestamp":1558132596327,"result":"14"}
您的 React 组件工作正常。
在 useEffect 将其设置为数据状态之前尝试记录后端结果。问题是您的结果,它可能不是数组,并且 map
函数仅适用于数组对象(在本例中)。
useEffect(() => {
axios
.get("http://127.0.0.1:8080")
.then(result => {
const items = result.data // It should be an array to map
console.log(items)
if (items && items.length) {
setData(items)
}
});
}, []);
我 understand/believe 我应该支持功能组件而不是 Class 组件,以便应用责任隔离。随着 React Hooks 的出现,它带来了一种新方法来避免 Class 并且仍然具有保持状态的功能。
我创建了一个非常简单的 Web Flux 休息服务。我使用 Hooks 和 Axon 创建了一个非常简单的 React 页面,我得到了 "data.map is not a function at RestApiHooksComponent"。如果我尝试这个使用典型休息服务的完全相同的代码,它将正常工作(我所说的典型休息服务是指没有任何反应功能的同步代码)。
我想我在用 Web Flux 计算 React 时遗漏了一些基本概念。关于如何通过 React 使用 Webflux 的示例很少,但没有一个示例显示如何使用 Hooks(至少我没有找到)。 Hooks 不符合 Reactive 范式吗?基于我对 React 有限的知识,我想这将是一个很好的匹配:常见的用户案例是使用流中的服务列表并在可用时显示它。
前端
package.json
...
"dependencies": {
"axios": "^0.18.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "3.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
...
App.js
import React, { useEffect, useState } from "react";
import axios from "axios";
export default function RestApiHooksComponent() {
const [data, setData] = useState([]);
useEffect(() => {
axios
.get("http://127.0.0.1:8080")
.then(result => setData(result.data));
}, []);
return (
<div>
<ul>
{data.map(item => (
<li key={item.result}>
{item.result}: {item.result}
</li>
))}
</ul>
</div>
);
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
LoanTable.js
import React from 'react'
const LoanTable = props => (
<table>
<thead>
<tr>
<th>Result</th>
</tr>
</thead>
<tbody>
{props.loans.length > 0 ? (
props.loans.map(loan => (
<tr>
<td>{loan.result}</td>
</tr>
))
) : (
<tr>
<td>No loans</td>
</tr>
)}
</tbody>
</table>
)
export default LoanTable
Webflux 休息服务
控制器:
@RestController
public class LoansController {
@Autowired
private LoansService loansService;
@CrossOrigin
@RequestMapping(method = RequestMethod.GET, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
@ResponseBody
public Flux<Loans> findAll() {
Flux<Loans> loans = loansService.findAll();
return loans;
}
}
服务:
@Service
public class LoansService implements ILoansService {
@Autowired
private LoansRepository loansRepository;
@Override
public Flux<Loans> findAll() {
return loansRepository.findAll();
}
}
Webflux 配置
@Configuration
@EnableWebFlux
public class WebFluxConfig implements WebFluxConfigurer
{
}
没有太多相关的细节,但我添加了它以表明它是完全无阻塞代码:
MongoDb 配置
@Configuration
@EnableReactiveMongoRepositories(basePackages = "com.mybank.web.repository")
public class MongoConfig extends AbstractReactiveMongoConfiguration
{
@Value("${port}")
private String port;
@Value("${dbname}")
private String dbName;
@Override
public MongoClient reactiveMongoClient() {
return MongoClients.create();
}
@Override
protected String getDatabaseName() {
return dbName;
}
@Bean
public ReactiveMongoTemplate reactiveMongoTemplate() {
return new ReactiveMongoTemplate(reactiveMongoClient(), getDatabaseName());
}
}
Pom:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mybank</groupId>
<artifactId>web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>web</name>
<description>webflux</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
*** 已编辑
网络通量服务输出
data:{"timestamp":1558126555269,"result":"8"} data:{"timestamp":1558132444247,"result":"10"} data:{"timestamp":1558132477916,"result":"10"} data:{"timestamp":1558132596327,"result":"14"}
您的 React 组件工作正常。
在 useEffect 将其设置为数据状态之前尝试记录后端结果。问题是您的结果,它可能不是数组,并且 map
函数仅适用于数组对象(在本例中)。
useEffect(() => {
axios
.get("http://127.0.0.1:8080")
.then(result => {
const items = result.data // It should be an array to map
console.log(items)
if (items && items.length) {
setData(items)
}
});
}, []);