简体   繁体   中英

React: what's a good pattern to extract the same logic in different components?

I'm building a React application and I'm curious about one thing. I have to render a list of elements, those elements can change in nature and type of data but they share a very similar logic. Here's two of them as example:

import React, { useState } from "react";
import JSONPretty from "react-json-prettify";
import Card from "react-bootstrap/Card";
import Accordion from "react-bootstrap/Accordion";
import Button from "react-bootstrap/Button";
import ListGroup from "react-bootstrap/ListGroup";

import TransactionState from "components/TransactionState";

import timestampToDate from "helpers/timestampToDate";

function BtcTransaction({ transaction }) {
    const [icon, setIcon] = useState("+");

    const toggleIcon = () => {
        const newIcon = icon === "+" ? "-" : "+";
        setIcon(newIcon);
    };

    return (
        <Card className="transaction-card">
            <Accordion>
                <Card.Body>
                    <Card.Title className="space-between">
                        <span>
                            BTC transaction -
                            <TransactionState state={transaction.state} />
                        </span>
                        <Accordion.Toggle
                            as={Button}
                            variant="primary"
                            size="sm"
                            eventKey="0"
                            onClick={() => toggleIcon()}
                        >
                            {icon}
                        </Accordion.Toggle>
                    </Card.Title>
                    <Card.Text></Card.Text>
                    <ListGroup variant="flush">
                        <ListGroup.Item>
                            <b>From: </b>
                            {transaction.from}{" "}
                        </ListGroup.Item>
                        <ListGroup.Item>
                            <b>To: </b>
                            {transaction.to}
                        </ListGroup.Item>
                        <ListGroup.Item>
                            <b>At: </b>
                            {timestampToDate(transaction.insertedAt)}
                        </ListGroup.Item>
                        <ListGroup.Item>
                            <b>Amount: </b>
                            {transaction.wholeAmount} BTC
                        </ListGroup.Item>
                    </ListGroup>
                    <Accordion.Collapse eventKey="0">
                        <JSONPretty json={transaction} />
                    </Accordion.Collapse>
                </Card.Body>
            </Accordion>
        </Card>
    );
}

export default BtcTransaction;

and here's another one:

import React, { useState } from "react";
import JSONPretty from "react-json-prettify";
import Card from "react-bootstrap/Card";
import Accordion from "react-bootstrap/Accordion";
import Button from "react-bootstrap/Button";
import ListGroup from "react-bootstrap/ListGroup";

import TransactionState from "components/TransactionState";

import timestampToDate from "helpers/timestampToDate";

function CustodialTransaction({ transaction }) {
    const [icon, setIcon] = useState("+");

    const toggleIcon = () => {
        const newIcon = icon === "+" ? "-" : "+";
        setIcon(newIcon);
    };

    return (
        <Card className="transaction-card">
            <Accordion>
                <Card.Body>
                    <Card.Title className="space-between">
                        <span>
                            Custodial transaction -
                            <TransactionState state={transaction.state} />
                        </span>
                        <Accordion.Toggle
                            as={Button}
                            variant="primary"
                            size="sm"
                            eventKey="0"
                            onClick={() => toggleIcon()}
                        >
                            {icon}
                        </Accordion.Toggle>
                    </Card.Title>
                    <Card.Text></Card.Text>
                    <ListGroup variant="flush">
                        <ListGroup.Item>
                            <b>Pair: </b>
                            {transaction.pair}
                        </ListGroup.Item>
                        <ListGroup.Item>
                            <b>Type: </b>
                            {transaction.type}
                        </ListGroup.Item>
                        <ListGroup.Item>
                            <b>At: </b>
                            {timestampToDate(transaction.insertedAt)}
                        </ListGroup.Item>
                        <ListGroup.Item>
                            <b>Amount: </b>
                            {transaction.fiatValue} {transaction.fiatCurrency}
                        </ListGroup.Item>
                    </ListGroup>
                    <Accordion.Collapse eventKey="0">
                        <JSONPretty json={transaction} />
                    </Accordion.Collapse>
                </Card.Body>
            </Accordion>
        </Card>
    );
}

export default CustodialTransaction;

Let's consider the logic to toggle the details with the function: toggleIcon and the state icon . It's exactly the same in both components.

To have a cleaner component I would like to extract that logic but I'm not sure what's a good pattern to do that.

Here's how I render the list based on the different types:

import React from "react";
import {
    BTC_TRANSACTION,
    CUSTODIAL_TRANSACTION,
} from "const/const";

import BtcTransaction from "components/BtcTransaction";
import CustodialTransaction from "components/CustodialTransaction";

import generateId from "helpers/generateId";

function TransactionsList({ data }) {
    return (
        <div>
            {data.map((el) => {
                switch (el.transactionType) {
                    case BTC_TRANSACTION:
                        return <BtcTransaction key={el.id} transaction={el} />;
                    case CUSTODIAL_TRANSACTION:
                        return <CustodialTransaction key={el.id} transaction={el} />;
                    default:
                        return <div key={generateId()}>Unrecognized transaction type</div>;
                }
            })}
        </div>
    );
}

export default TransactionsList;

How can I do that?

u can create a hook to handle icons, a hook that returns an iconToggler function, and an icon state, something like this:

const useIcon = () => {
  const [icon, setIcon] = React.useState('+');

  const toggleIcon = () => setIcon((prev) => (prev === '+' ? '-' : '+'));
  return {
    icon,
    toggleIcon,
  };
};

function App() {
  const { icon, toggleIcon } = useIcon();
  return <button onClick={toggleIcon}>{icon}</button>;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM