[FE] Use id field for keys

This commit is contained in:
Azat Mutigullin 2020-11-12 18:21:21 +03:00
parent cba12d4ff4
commit be4b9543ce
8 changed files with 32 additions and 17 deletions

View file

@ -12,6 +12,11 @@ interface Props {
offlineClusters: Cluster[]; offlineClusters: Cluster[];
} }
interface ChunkItem {
id: string;
data: Cluster[];
}
const ClustersWidget: React.FC<Props> = ({ const ClustersWidget: React.FC<Props> = ({
clusters, clusters,
onlineClusters, onlineClusters,
@ -19,14 +24,17 @@ const ClustersWidget: React.FC<Props> = ({
}) => { }) => {
const [showOfflineOnly, setShowOfflineOnly] = React.useState<boolean>(false); const [showOfflineOnly, setShowOfflineOnly] = React.useState<boolean>(false);
const clusterList: Array<Cluster[]> = React.useMemo(() => { const clusterList: ChunkItem[] = React.useMemo(() => {
let list = clusters; let list = clusters;
if (showOfflineOnly) { if (showOfflineOnly) {
list = offlineClusters; list = offlineClusters;
} }
return chunk(list, 2); return chunk(list, 2).map((data) => ({
id: v4(),
data,
}));
}, [clusters, offlineClusters, showOfflineOnly]); }, [clusters, offlineClusters, showOfflineOnly]);
const handleSwitch = () => setShowOfflineOnly(!showOfflineOnly); const handleSwitch = () => setShowOfflineOnly(!showOfflineOnly);
@ -56,8 +64,8 @@ const ClustersWidget: React.FC<Props> = ({
</MetricsWrapper> </MetricsWrapper>
{clusterList.map((chunkItem) => ( {clusterList.map((chunkItem) => (
<div className="columns" key={v4()}> <div className="columns" key={chunkItem.id}>
{chunkItem.map((cluster) => ( {chunkItem.data.map((cluster) => (
<ClusterWidget cluster={cluster} key={cluster.id} /> <ClusterWidget cluster={cluster} key={cluster.id} />
))} ))}
</div> </div>

View file

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import { v4 } from 'uuid';
import { ClusterName, TopicName, TopicConfig } from 'redux/interfaces'; import { ClusterName, TopicName, TopicConfig } from 'redux/interfaces';
interface Props { interface Props {
@ -57,7 +56,7 @@ const Sertings: React.FC<Props> = ({
</thead> </thead>
<tbody> <tbody>
{config.map((item) => ( {config.map((item) => (
<ConfigListItem key={v4()} config={item} /> <ConfigListItem key={item.id} config={item} />
))} ))}
</tbody> </tbody>
</table> </table>

View file

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import { v4 } from 'uuid';
import { TopicWithDetailedInfo, ClusterName } from 'redux/interfaces'; import { TopicWithDetailedInfo, ClusterName } from 'redux/interfaces';
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb'; import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
@ -60,7 +59,7 @@ const List: React.FC<Props> = ({ clusterName, topics, externalTopics }) => {
</thead> </thead>
<tbody> <tbody>
{items.map((topic) => ( {items.map((topic) => (
<ListItem key={v4()} topic={topic} /> <ListItem key={topic.id} topic={topic} />
))} ))}
</tbody> </tbody>
</table> </table>

View file

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import { v4 } from 'uuid';
interface Link { interface Link {
label: string; label: string;
@ -17,7 +16,7 @@ const Breadcrumb: React.FC<Props> = ({ links, children }) => {
<ul> <ul>
{links && {links &&
links.map(({ label, href }) => ( links.map(({ label, href }) => (
<li key={v4()}> <li key={href}>
<NavLink to={href}>{label}</NavLink> <NavLink to={href}>{label}</NavLink>
</li> </li>
))} ))}

View file

@ -5,7 +5,7 @@ import {
BrokerMetrics, BrokerMetrics,
Cluster, Cluster,
Topic, Topic,
TopicConfig, InputTopicConfig,
TopicDetails, TopicDetails,
TopicMessage, TopicMessage,
TopicName, TopicName,
@ -54,7 +54,7 @@ export const fetchTopicConfigAction = createAsyncAction(
ActionType.GET_TOPIC_CONFIG__REQUEST, ActionType.GET_TOPIC_CONFIG__REQUEST,
ActionType.GET_TOPIC_CONFIG__SUCCESS, ActionType.GET_TOPIC_CONFIG__SUCCESS,
ActionType.GET_TOPIC_CONFIG__FAILURE ActionType.GET_TOPIC_CONFIG__FAILURE
)<undefined, { topicName: TopicName; config: TopicConfig[] }, undefined>(); )<undefined, { topicName: TopicName; config: InputTopicConfig[] }, undefined>();
export const createTopicAction = createAsyncAction( export const createTopicAction = createAsyncAction(
ActionType.POST_TOPIC__REQUEST, ActionType.POST_TOPIC__REQUEST,

View file

@ -4,7 +4,7 @@ import {
Topic, Topic,
ClusterName, ClusterName,
TopicDetails, TopicDetails,
TopicConfig, InputTopicConfig,
TopicFormData, TopicFormData,
TopicFormCustomParam, TopicFormCustomParam,
TopicFormFormattedParams, TopicFormFormattedParams,
@ -30,7 +30,7 @@ const formatCustomParams = (
export const getTopicConfig = ( export const getTopicConfig = (
clusterName: ClusterName, clusterName: ClusterName,
topicName: TopicName topicName: TopicName
): Promise<TopicConfig[]> => ): Promise<InputTopicConfig[]> =>
fetch(`${BASE_URL}/clusters/${clusterName}/topics/${topicName}/config`, { fetch(`${BASE_URL}/clusters/${clusterName}/topics/${topicName}/config`, {
...BASE_PARAMS, ...BASE_PARAMS,
}).then((res) => res.json()); }).then((res) => res.json());

View file

@ -5,12 +5,16 @@ export enum CleanupPolicy {
Compact = 'compact', Compact = 'compact',
} }
export interface TopicConfig { export interface InputTopicConfig {
name: string; name: string;
value: string; value: string;
defaultValue: string; defaultValue: string;
} }
export interface TopicConfig extends InputTopicConfig {
id: string;
}
export interface TopicConfigByName { export interface TopicConfigByName {
byName: { byName: {
[paramName: string]: TopicConfig; [paramName: string]: TopicConfig;
@ -89,6 +93,7 @@ export interface TopicFormCustomParams {
export interface TopicWithDetailedInfo extends Topic, TopicDetails { export interface TopicWithDetailedInfo extends Topic, TopicDetails {
config?: TopicConfig[]; config?: TopicConfig[];
id: string;
} }
export interface TopicsState { export interface TopicsState {

View file

@ -1,3 +1,4 @@
import { v4 } from 'uuid';
import { Action, TopicsState, Topic } from 'redux/interfaces'; import { Action, TopicsState, Topic } from 'redux/interfaces';
import ActionType from 'redux/actionType'; import ActionType from 'redux/actionType';
@ -18,6 +19,7 @@ const updateTopicList = (state: TopicsState, payload: Topic[]): TopicsState => {
memo.byName[name] = { memo.byName[name] = {
...memo.byName[name], ...memo.byName[name],
...topic, ...topic,
id: v4(),
}; };
memo.allNames.push(name); memo.allNames.push(name);
@ -30,7 +32,7 @@ const addToTopicList = (state: TopicsState, payload: Topic): TopicsState => {
...state, ...state,
}; };
newState.allNames.push(payload.name); newState.allNames.push(payload.name);
newState.byName[payload.name] = payload; newState.byName[payload.name] = { ...payload, id: v4() };
return newState; return newState;
}; };
@ -61,7 +63,10 @@ const reducer = (state = initialState, action: Action): TopicsState => {
...state.byName, ...state.byName,
[action.payload.topicName]: { [action.payload.topicName]: {
...state.byName[action.payload.topicName], ...state.byName[action.payload.topicName],
config: action.payload.config, config: action.payload.config.map((inputConfig) => ({
...inputConfig,
id: v4(),
})),
}, },
}, },
}; };