Zokulko
Zokulko

Reputation: 229

REACT- How do I refactor my code to avoid mulitple declaration of variable (20 in total)

how can I optimize/ factor my code. I have two cases 'exist' and 'new',depending on this value I made a different display. At the beginning I've just create a general value called user, pwd ... (18 more) but the issue was that when I fill a field (user for example) for case 'exist' then the field 'user' is filled too - for the other case (i.e new) although they should not be the same.

Is there a way to factor my code please ?

export default function Choice() {

    const [existUser, setExistUser] = useState("");
    const [newUser, setNewUser] = useState("");
    const [existPwd, setPwd] = useState("");
    const [newPwd, setPwd] = useState("");
    // There are 20 in total with the exact same pattern (exist and new)
    ...
    let handleSubmit = async (e) => {
        e.preventDefault();
        try {
            let res = await fetch("", {
                method: "POST",
                body: JSON.stringify({
                    existUser: existUser,
                    ...
                }),
            });
            let resJson = await res.json();
            if (res.status === 200) {
                setExistUser("");
               ...
    setRadio(false)
    message("Success")
} else {
    setMessage("Error");
}
        } catch (err) {
    console.log(err);
}
    };


return (
    < >
        <form onSubmit={handleSubmit}>
            <input type="radio" value='exist' onChange={(e) => setRadio(e.target.value)} checked={radio === 'exist'} />
            <input type="radio" value='new' onChange={(e) => setRadio(e.target.value)} checked={radio === 'new'} />
        </form>
        {(radio === 'exist') ?
            (
                <div>
                    <form onSubmit={handleSubmit}>
                        <label >
                            User:
                            <input
                                type="text"
                                value={existUser}
                                placeholder="User"
                                onChange={(e) => setExistUser(e.target.value)}
                            />
                        </label>
                    </form>
                </div>
            )
            :
            ('')
        }

        {
            (radio === 'new') ?
                (<div>
                    <form onSubmit={handleSubmit}>
                            //Other inputs with the exct same pattren as before - newUser...
                        <inputs .../>
                    </form>
                </div>
                )
                :
                ('')
        }

        {(radio === ('exist')) ?
            <div>
                <form onSubmit={handleSubmit}>
                        // Exact same inputs to display as radio==='new' (above)
                </form >
            </div>
            :
            ('')
        }

    </>
)
}

Here is my code

Upvotes: 2

Views: 522

Answers (2)

Isaac Batista
Isaac Batista

Reputation: 107

You could create two contexts with the states you need: user, pwd, etc. The repeated structure could be encapsulated with a factory.

Full example on sandbox, but you will get something like this:

// FormContext.jsx
import React, { useState } from 'react';

// the two contexts you need
export const NewFormContext = React.createContext();
export const ExistFormContext = React.createContext();

// the factory that creates a provider with the same states
// but, independent and linked to different contexts
const makeFormProvider = (FormContext) => ({ children }) => {
  const [user, setUser] = useState("");
  const [pwd, setPwd] = useState("");
  
  return (
    <FormContext.Provider value={{pwd,user, setUser, setPwd}}>
      {children}
    </FormContext.Provider>
  );
};

export const NewFormProvider = makeFormProvider(NewFormContext);
export const ExistFormProvider = makeFormProvider(ExistFormContext);

Then, inside the component in which you should render the Choice.jsx - thats App.jsx in my case - after you provide the contexts to your component

  // App.jsx
  <NewFormProvider>
    <ExistFormProvider>
      <Choice />
    </ExistFormProvider>
  </NewFormProvider>

Just consume them

// Choice.jsx
const newForm = useContext(NewFormContext)
const existForm = useContext(ExistFormContext)

const handleSubmit = () => {
  // handle it here
}

return (
    <div>
        <form onSubmit={handleSubmit}>
          <input type="text" id="new-user" value={newForm.user} onChange={(e) => newForm.setUser(e.target.value)} />
          <input type="text" id="exist-user" value={existForm.user} onChange={(e) => existForm.setUser(e.target.value)}/>
        </form>
    </div>
  )

P.S.: Take care when reusing code like this. Be sure these two forms will change for the same reason, respecting the SRP. If they won't, just use two separate structures from the beginning.

Upvotes: 2

Josh Lin
Josh Lin

Reputation: 2437

I believe there are mainly 2 ways to refactor this many-states-problem

  1. use object state instead of single string state
  2. use custom hook to make two states seems one

1. use object state instead of single string state

const [radio, setRadio] = useState("");
const [existValues, setExist] = useState({});
const [newValues, setNew] = useState({});

2. use custom hook to make two states seems one (affected by context)

let [user, setUser] = useChoicValue();
let [pwd, setPwd] = useChoicValue();

for this two main way, each you can make other optimize, for example, bring reusable codes for each subject into a template, then reuse the template with subject specific props, or mix some good points from these ways

3. resusable subject template

const ExistOrNew = ({
  radio,
  name,
  existValue,
  newValue,
  updateExist,
  updateNew
}) => {

4. use context to reduce prop drill

const ExistOrNew = ({
    radio,
    name,
    keyExist,
    keyNew
  }) => {
    let newCtx = useContext(NewCtx);
    let existCtx = useContext(ExistCtx);

5. use a lib that's good at handle states

things like https://react-hook-form.com/get-started


So I made 4 example refactor

  1. use object state instead of string state + template: https://codesandbox.io/s/determined-dewdney-c2ypv4?file=/src/components/Refactor2Comps1.jsx
  2. object + template + context: https://codesandbox.io/s/determined-dewdney-c2ypv4?file=/src/components/Refactor2Comps2.jsx
  3. lib + template: https://codesandbox.io/s/determined-dewdney-c2ypv4?file=/src/components/RefactorLib.jsx
  4. custom hook: https://codesandbox.io/s/determined-dewdney-c2ypv4?file=/src/components/RefactorHook.jsx:434-514

and working codes here, try it yourself https://codesandbox.io/s/determined-dewdney-c2ypv4?file=/src/App.js

enter image description here

Upvotes: 0

Related Questions