Rumah  >  Artikel  >  hujung hadapan web  >  Reaksi: Boleh Digunakan Semula, Dipisahkan dan Diasingkan

Reaksi: Boleh Digunakan Semula, Dipisahkan dan Diasingkan

WBOY
WBOYasal
2024-08-19 17:15:031069semak imbas

Apabila mengerjakan halaman baharu, jangan hanya mula menulis komponen pada halaman itu sendiri. Mula memperibadikan unit komponen supaya setiap satu cukup bebas dan perubahan pada keadaan global tidak akan menyebabkan pemaparan semula. Contohnya, bayangkan anda diberi tugas untuk mencipta halaman ini.

React: Reusable, Decoupled, and Isolated

Anda mungkin tergoda, seperti saya semasa belajar kod, untuk menulis semua komponen ini dalam satu fail.

// WRONG
export const LoginPage = () => {
  const [userId, setUserId] = useState('')
  const [password, setPassword] = useState('')

  return (
    <div>
      <div className="d-flex flex-row">
        <div id="marketing-container">
          <svg {...fbLogo} />
          <h2>Connect with friends and the world around you on Facebook</h2>
        </div>
        <div id="login-form">
          <div>
            <input id="user-id" value={userId} onChange={(e) => setUserId(e.target.value)} />
            <input id="password" value={password} onChange={(e) => setPassword(e.target.value)} />
            <button>Log in</button>
            <a href="/forgot-password">Forgot password</a>
            <br />
            <button>Create new account</button>
          </div>
          <div>
            <strong>Create a page</strong>
            <span>for a celebrity, brand or business.</span>
          </div>
        </div>
      </div>
    </div>
  )
}

export default LoginPage

Tetapi realitinya ialah banyak elemen ini mungkin akan digunakan semula sepanjang apl pada masa hadapan dan ini bermakna ia perlu ditulis semula atau disalin/tampal. Apabila bekerja dengan reka bentuk yang jelas, kemungkinan elemen akan digunakan seperti lego, logo yang ditunjukkan pada halaman log masuk akan sama dengan yang terdapat pada skrin papan pemuka, mungkin hanya saiz yang berbeza sudah tentu. Sama dengan input id pengguna, dari segi reka bentuk ia mungkin akan sama seperti yang terdapat pada halaman edit pengguna.

Yang membawa saya ke perkara seterusnya, komponen harus dipisahkan daripada logik perniagaan kepada logik pembentangan. Apa yang saya maksudkan dengan ini ialah bahagian yang berkomunikasi dengan negeri haruslah komponennya sendiri dan komponen ini hanya akan menghantar prop pembentangan kepada komponen pembentangan.

// Presentational component
const UserIdInputComponent = ({ value, onChange }) =>
  <input value={value} onChange={onChange} />

// Logic component
export const UserIdInput = () => {
  const [value, setValue] = useState('')

  const handleChange = (e) => setValue(e.target.value)

  return <UserIdInputComponent value={value} onChange={handleChange} />
}

Ini membolehkan alatan seperti buku cerita berfungsi dengan betul dengan hanya mengeksport komponen pembentangan yang diasingkan daripada pengurusan negeri. Ia boleh menjengkelkan jika anda perlu menyepadukan komponen berat logik pada buku cerita, yang membuat panggilan api, membuat perubahan keadaan global. Dengan pendekatan ini, anda boleh melihat bagaimana komponen akan berubah secara visual berdasarkan prop yang berbeza.

Kembali ke halaman utama. Anda mungkin dapat melihat ke mana saya pergi dengan ini. Daripada menulis semuanya pada halaman yang sama. Fikirkan bagaimana komponen ini boleh digunakan semula, bagaimana ia boleh dipisahkan daripada keadaan dan bagaimana ia boleh diasingkan supaya ia tidak pernah dipaparkan semula melainkan prop yang berkaitan dengan komponen ini berubah.

export const LoginPage = () => (
  <div>
    <div className="d-flex flex-row">
      <FbMarketing />
      <LoginForm />
    </div>
  </div>
)

export default LoginPage

Senario kes terbaik begini cara anda mula mengekodnya. Ia akan menjadi lebih rumit untuk kembali dan memfaktorkannya semula sebaik sahaja anda melihat bahawa semuanya berfungsi seperti yang diharapkan. Saya bersalah kerana mahu melihat sesuatu dengan pantas pada skrin, menenangkan kebimbangan dan mula membina struktur yang betul dari awal.

const FbLogo = () => (
  <svg {...fbLogoAttributes} />
)

const FbSlogan = () => (
  <h2>Connect with friends and the world around you on Facebook.</h2>
)

const FbMarketing = () => (
  <>
    <FbLogo />
    <FbSlogan />
  </>
)

Berikut adalah semua pembentangan pada ketika ini. Ini boleh diindividukan lagi sebagai FbLogoSmall, FbLogoMedium dan sebagainya.

Kini bahagian yang mengandungi beberapa logik, borang log masuk. Tidak pasti sama ada log masuk, log masuk atau log masuk tetapi kami akan meneruskan dan menggunakan istilah Facebook, ‘log masuk’.

Sebagai peringatan, setiap komponen hendaklah boleh diguna semula, dipisahkan dan diasingkan.

Boleh Guna Semula:

Mula-mula mari jadikan UserIdInput boleh diguna semula dan kemudian salin pendekatan ini ke input kata laluan yang lain: Perlu diingat bahawa input tahap pengeluaran akan termasuk atribut lain seperti id ujian, kelas yang berubah berdasarkan prop, atribut aria, autofokus, begitu banyak lebih banyak prop/atribut lain bergantung pada alatan yang digunakan oleh pangkalan kod. Jika seseorang memberitahu anda ia lebih kompleks daripada apa yang saya tulis di sini, dengarkan orang itu.

// UserIdInput.js
import { useContext, createContext } from "react";

export const UserIdContext = createContext();

const UserIdInput = () => {
  const { userId, setUserId } = useContext(UserIdContext);

  const handleChange = (e) => {
    setUserId(e.target.value);
  };

  return <UserIdInputComponent value={userId} onChange={handleChange} />;
};

Kini input ini boleh digunakan semula pada borang edit pengguna sebagai contoh. Beginilah rupa input kata laluan:

// PasswordInput.js
import { useContext, createContext } from "react";

export const PasswordContext = createContext();

const PasswordInput = () => {
  const { password, setPassword } = useContext(PasswordContext);

  const handleChange = (e) => {
    setPassword(e.target.value);
  };

  return (
    <div>
      <PasswordInputComponent value={password} onChange={handleChange} />
    </div>
  );
};

Dipisahkan:

Dipisahkan dalam erti kata mengasingkan logiknya daripada bahagian pembentangannya, 'logik perniagaan' vs visual: Di sini kita dapat melihat bahawa prop diluluskan tanpa pengubahsuaian atau definisi fungsi baharu di tengah, malah saya memulangkan jsx terus ke atas tanpa kata kunci pulangan. Sekali lagi, jika seseorang memberitahu anda ia lebih rumit daripada ini, ia adalah… label hendaklah komponen mereka sendiri, input juga.

// UserIdInputComponent.js
const UserIdInputComponent = ({ value, onChange }) => (
  <div>
    <label>User Id:</label>
    <input type="text" value={value} onChange={onChange} required />
  </div>
);
// PasswordInputComponent.js
const PasswordInputComponent = ({ value, onChange }) => (
  <div>
    <label>Password:</label>
    <input type="password" value={value} onChange={onChange} required />
  </div>
);

Terpencil:

Kami telah menjaga bahagian terpencil dengan mencipta konteks, kini apabila kami menukar mana-mana input, input lain tidak akan dipaparkan semula. Satu-satunya elemen yang akan diberikan semula ialah input yang ditukar dan butang log masuk. Ini adalah penunjuk yang baik untuk menentukan sama ada apl tindak balas anda dioptimumkan dengan betul, kadangkala pengoptimuman pramatang adalah baik. Meningkatkan kemahiran pasukan.

const LoginButton = () => {
  const { userId } = useContext(UserIdContext);
  const { password } = useContext(PasswordContext);

  const onClick = (e) => {
    e.preventDefault();
    console.log("form submit", userId, password)
  };

  return <button onClick={onClick}>Log in</button>;
};

Kecuali! Ini sebenarnya tidak berlaku, saya cuba menggunakan konteks untuk mengasingkan perubahan tetapi apabila ia turun untuk berkongsi userId dan kata laluan saya terpaksa menggunakan redux kerana sebaik sahaja saya menggunakan UserIdProvider untuk membungkus Butang Masuk, ia mencipta keadaan baru dengan userId dan kata laluan baharu . Beginilah rupanya dengan redux.

// LoginButton.js
import { useSelector } from "react-redux";

const LoginButton = () => {
  const { userId, password } = useSelector(state => state)

  const onClick = (e) => {
    e.preventDefault();
    console.log("form submit", userId, password);
  };

  return <button onClick={onClick}>Log in</button>;
};

export default LoginButton

Mungkin sepatutnya menaipnya sebelum ini tetapi inilah kedai reduxnya.

// store.js
import { createSlice, configureStore } from '@reduxjs/toolkit'

const login = createSlice({
  name: 'login',
  initialState: {
    userId: '',
    password: '',
  },
  reducers: {
    userId: (state, action) => {
      state.userId = action.payload
    },
    password: (state, action) => {
      state.password = action.payload
    }
  }
})

export const { userId: setUserId, password: setPassword } = login.actions

export const store = configureStore({
  reducer: login.reducer
})

Berjanji dengan diri saya sendiri dengan redux tetapi ia hanya berfungsi dengan baik untuk mengasingkan perubahan supaya ia meminimumkan pemaparan semula. Biasanya saya tidak akan terlalu mempercayai seseorang yang mengelak daripada memaparkan semula pada semua kos tetapi itu hanya petunjuk yang baik untuk kod tindak balas yang baik.

React: Reusable, Decoupled, and Isolated

Here are the updated files for the two inputs. Not a lot changed but pay attention to how easy it was for me to change only the business logic component. Changed the value selector, the handleChange function and that was it. This is one of the advantages of decoupling, it’s not that obvious with such a small component but a codebase that uses complex logic I can see how this approach can be beneficial.

// UserIdInput.js (revised final)
import { setUserId } from "./store";
import { useDispatch, useSelector } from "react-redux";

const UserIdInputComponent = ({ value, onChange }) => (
  <div>
    <label>User Id:</label>
    <input type="text" value={value} onChange={onChange} required />
  </div>
);

const UserIdInput = () => {
  const userId = useSelector(({ userId }) => userId)
  const dispatch = useDispatch()

  const handleChange = (e) => {
    dispatch(setUserId(e.target.value))
  };

  return <UserIdInputComponent value={userId} onChange={handleChange} />;
};
// PasswordInput.js (revised final)
import { useDispatch, useSelector } from "react-redux";
import { setPassword } from "./store";

const PasswordInputComponent = ({ value, onChange }) => (
  <>
    <label>Password:</label>
    <input type="password" value={value} onChange={onChange} required />
  </>
);

const PasswordInput = () => {
  const password = useSelector(({ password }) => password)
  const dispatch = useDispatch()

  const handleChange = e => {
    dispatch(setPassword(e.target.value))
  };

  return <PasswordInputComponent value={password} onChange={handleChange} />
};

The result should only highlight updates on the changed input and the login button itself like so:

React: Reusable, Decoupled, and Isolated

There’s a problem though, the labels are also updating. Let’s fix that really quick just to prove the point of over, but potentially necessary optimization. Up to your discretion.

// UserIdInput.js
import { setUserId } from "./store";
import { useDispatch, useSelector } from "react-redux";

const UserIdInputComponent = ({ value, onChange }) => (
  <input type="text" value={value} onChange={onChange} required />
);

const UserIdInput = () => {
  const userId = useSelector(({ userId }) => userId)
  const dispatch = useDispatch()

  const handleChange = (e) => {
    dispatch(setUserId(e.target.value))
  };

  return <UserIdInputComponent value={userId} onChange={handleChange} />;
};

// separated the label from the logic heavy component
export const UserIdInputWithLabel = () => (
  <div>
    <label>User id: </label>
    <UserIdInput />
  </div>
)

export default UserIdInputWithLabel

Here is the password input.

// PasswordInput.js
import { useDispatch, useSelector } from "react-redux";
import { setPassword } from "./store";

const PasswordInputComponent = ({ value, onChange }) => (
  <input type="password" value={value} onChange={onChange} required />
);

const PasswordInput = () => {
  const password = useSelector(({ password }) => password)
  const dispatch = useDispatch()

  const handleChange = e => {
    dispatch(setPassword(e.target.value))
  };

  return <PasswordInputComponent value={password} onChange={handleChange} />
};

// separated label from logic heavy component
const PasswordInputWithLabel = () => (
  <div>
    <label>Password: </label>
    <PasswordInput />
  </div>
)

export default PasswordInputWithLabel

This approach yields the following results:

React: Reusable, Decoupled, and Isolated

Fully optimized.

Available here: https://github.com/redpanda-bit/reusable-decoupled-isolated

Conclusion

There you have it, reusable, decoupled, and isolated react components. Very small example but hope that it gives you an idea of how production grade react applications look like. It may be disappointing for some to see all the work that goes into creating a good react component, but I’ll tell ya, once you are faced with a huge form that has complex elements and possibly some animation you will see positive gains on speed. The last thing you want is an input lagging behind in front of a 100 words per minute types.

References:

https://nextjs.org/

https://redux.js.org/

Atas ialah kandungan terperinci Reaksi: Boleh Digunakan Semula, Dipisahkan dan Diasingkan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn