簡體   English   中英

React中,父子組件之間如何通過useContext共享state?

[英]In React, How to share state between parent and child components with useContext?

為了共享 state 值,我制作了一個帶有上下文的 ContextProvider。

我的 ContextProvider 是這樣的。

import React, {createContext, PropsWithChildren, useState} from "react";
import {ISignUpResult} from "amazon-cognito-identity-js";

export type CreateCognitoUserContextType = {
  id: string,
  setId: (id: string) => void,
  password: string,
  setPassword: (password: string) => void,
  email: string,
  setEmail: (email: string) => void,
  phone: string,
  setPhone: (phone: string) => void,
  result: ISignUpResult | null,
  setResult: (result: ISignUpResult | null) => void,
}

const CreateCognitoUserContext = createContext<CreateCognitoUserContextType>({
  id: "",
  setId: (id: string) => {
  },
  password: "",
  setPassword: (password: string) => {
  },
  email: "",
  setEmail: (email: string) => {
  },
  phone: "",
  setPhone: (phone: string) => {
  },
  result: null,
  setResult: (result: ISignUpResult | null) => {
  },
})
CreateCognitoUserContext.displayName = "CreateCognitoUserContext"

const CreateCognitoUserProvider = ({children}: PropsWithChildren) => {
  const [id, setId] = useState<string>("");
  const [password, setPassword] = useState<string>("");
  const [email, setEmail] = useState<string>("");
  const [phone, setPhone] = useState<string>("");
  const [result, setResult] = useState<ISignUpResult | null>(null);

  return (
    <CreateCognitoUserContext.Provider
      value={{
        id: id,
        setId: (id: string) => {
          setId(id);
        },
        password: password,
        setPassword: (password: string) => {
          setPassword(password);
        },
        email: email,
        setEmail: (email: string) => {
          setEmail(email);
        },
        phone: phone,
        setPhone: (phone: string) => {
          setPhone(phone);
        },
        result: result,
        setResult: (result: ISignUpResult | null) => {
          setResult(result);
        }
      }}
    >
      {children}
    </CreateCognitoUserContext.Provider>
  );
}

export {CreateCognitoUserProvider}
export default CreateCognitoUserContext

而且,組件的樹是這樣的。

SignUpView
    - SignUpStepBaseView (this view has a next and prev button)
        - CreateCognitoUserView

這是Stepper布局。 SignUpView是根組件。 CreateCognitoUserView中,有四個名為 id、password、email 和 phone 的文本字段。 我將那些文本字段的 onChange 屬性設置為上下文提供者的設置器。 然后,當用戶按下SignUpStepBaseView中的下一步按鈕時,視圖調用控制器的 function 並將上下文提供程序的值作為參數發送。 但是,當填寫完所有文本字段后按下一個按鈕時,上下文提供程序的值都是空的。

這是剩下的代碼。

// SignUpView
export default function SignUpView() {
  const {
    createCognitoUser,
  } = CreateCognitoUserController()
  const {step, steps, next, back} = SignUpController()
  const createCognitoUserContextType = useContext<CreateCognitoUserContextType>(CreateCognitoUserContext)

  const handleFirstNext = async () => {
    console.log(`id: ${createCognitoUserContextType.id}, password: ${createCognitoUserContextType.password}, email: ${createCognitoUserContextType.email}, phone: ${createCognitoUserContextType.phone}`)

    createCognitoUserContextType.setResult(
      await createCognitoUser(
        createCognitoUserContextType.id,
        createCognitoUserContextType.password,
        createCognitoUserContextType.email,
        createCognitoUserContextType.phone
      )
    )
    if (createCognitoUserContextType.result) {
      next()
    }
  }

  const getStepContent = (stepNumber: number) => {
    switch (stepNumber) {
      case 0:
        return (
          <SignUpStepBase step={stepNumber} next={handleFirstNext} children={<CreateCognitoUserView/>}/>
        );
      case 1:
        return (
          <SignUpStepBase step={stepNumber} next={next} back={back} children={<p>1111</p>}/>
        );
      case 2:
        return (
          <SignUpStepBase step={stepNumber} back={back} children={<p>2222</p>}/>
        );
      default:
        return (
          <p>??????</p>
        );
    }
  }

  return (
    <CreateCognitoUserProvider>
      <div className="Sign-Up-View" style={{height: "100%", width: "100%", display: "flex", flexDirection: "column"}}>
        <div className="Sign-Up-Stepper" style={{height: "5%"}}>
          <Stepper activeStep={step}>
            {steps.map((label, index) => (
              <Step key={`${label} ${index}`}>
                <StepLabel>
                  <p>{label}</p>
                </StepLabel>
              </Step>
            ))}
          </Stepper>
        </div>
        <div className="Large-Divider"/>
        <div className="Sign-Up-Body-Contents" style={{height: "95%", marginLeft: "12px", marginRight: "12px"}}>
          {getStepContent(step)}
        </div>
      </div>
    </CreateCognitoUserProvider>
  );
}
// createCognitoUserController
export default function CreateCognitoUserController() {
  const {baseEventHandle} = useHandleException<CognitoUser | any>()

  const createCognitoUserCase: CreateCognitoUserCase = new CreateCognitoUserCase(new UserRepositoryImpl(new UserApiDataSourceImpl()));

  async function createCognitoUser(id: string, password: string, email: string, phone: string): Promise<ISignUpResult | null> {
    let result: ISignUpResult | null = null

    await baseEventHandle({
      action: async () => {
        return await createCognitoUserCase.invoke(id, password, email, phone)
      },
      onSuccess: async (response: ISignUpResult) => {
        result = response
      },
      alertType: AlertType.Snackbar,
    })

    return result
  }

  return {
    createCognitoUser,
  }
}
// SignUpStepBaseView
export default function SignUpStepBase({step, next, back, children}: PropsWithChildren<SignUpStepBaseProps>) {
  const {steps} = SignUpController()

  const handleNext = async (event: React.MouseEvent<HTMLButtonElement>) => {
    event.preventDefault()
    next?.()
  }

  const handleBack = async (event: React.MouseEvent<HTMLButtonElement>) => {
    event.preventDefault()
    back?.()
  }

  return (
    <div className="Sign-Up-Process-Body" style={{height: "95%"}}>
      <div className="Sign-Up-Process-Contents" style={{height: "95%"}}>
        {children}
      </div>
      <div className="Sign-Up-Process-Buttons" style={{float: "right"}}>
        <Button
          style={{marginRight: '10px'}}
          variant="contained"
          color="primary"
          disabled={step === 0}
          onClick={handleBack}>
          {PREV_BUTTON_TEXT}
        </Button>
        <Button variant="contained" color="primary" onClick={handleNext}>
          {step === steps.length - 1 ? (
            CONFIRM_BUTTON_TEXT
          ) : (
            NEXT_BUTTON_TEXT
          )}
        </Button>
      </div>
    </div>
  );
}
// CreateCognitoUserView
export default function CreateCognitoUserView() {
  const createCognitoUserContextType = useContext<CreateCognitoUserContextType>(CreateCognitoUserContext)

  const onIdChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    event.preventDefault()
    createCognitoUserContextType.setId(event.target.value)
  }

  const onPasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    event.preventDefault()
    createCognitoUserContextType.setPassword(event.target.value)
  }

  const onEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    event.preventDefault()
    createCognitoUserContextType.setEmail(event.target.value)
  }

  const onPhoneChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    event.preventDefault()
    createCognitoUserContextType.setPhone(event.target.value)
  }

  return (
    <div className="Create-Cognito-User-View" style={{flexDirection: "column"}}>
      <div className={"Create-Cognito-User-View-Id"}>
        <TextField style={{width: "360px"}} id="create-cognito-user-id-field" label="id" variant="outlined" onChange={onIdChange}/>
      </div>
      <div className="Default-Divider"/>
      <div className={"Create-Cognito-User-View-Password"}>
        <TextField style={{width: "360px"}} id="create-cognito-user-password-field" type="password" label="password" variant="outlined" onChange={onPasswordChange}/>
      </div>
      <div className="Default-Divider"/>
      <div className={"Create-Cognito-User-View-Email"}>
        <TextField style={{width: "360px"}} id="create-cognito-user-email-field" label="email" variant="outlined" onChange={onEmailChange}/>
      </div>
      <div className="Default-Divider"/>
      <div className={"Create-Cognito-User-View-Phone"}>
        <TextField style={{width: "360px"}} id="create-cognito-user-phone-field" label="phone" variant="outlined" onChange={onPhoneChange}/>
      </div>
    </div>
  );
}

我與上下文提供者共享 state 值的解決方案是否錯誤...?

我已經通過一個非常簡單的更改解決了它。

我只是將CreateCognitoUserProvider的位置移動到App文件。

像這樣。

<Router>
  <Routes>
    <Route path="/*" element={<HomeView/>}>home</Route>
    <Route path="/login" element={<LoginView/>}>login</Route>
    <Route path="/sign-up" element={
      <CreateCognitoUserProvider>
        <SignUpView/>
      </CreateCognitoUserProvider>
    }>signup</Route>
  </Routes>
</Router>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM