繁体   English   中英

如何使用来自 React 的 useReducer 挂钩从两个不同的处理程序更新 state

[英]How to update state from two different handlers using useReducer hooks from React

我正在使用 { useReducer } 来管理我的表单的 state。但我有两个单独的处理程序

  1. handleChange() = 用于输入更改(这个按预期工作)

 const handleChange = async (e) => { dispatch({field: e.target.name, value: e.target.value}); }

  1. UploadFile() = 用于上传图像(这个不更新图像/状态)

 const uploadFile = async (e) => { console.log('Uploading file...'); const files = e.target.files; const data = new FormData(); data.append('file', files[0]); data.append('upload_preset', 'artemis'); const res = await fetch(`https://api.cloudinary.com/v1_1/${cludinaryAccount}/image/upload`, { method: 'POST', body: data }); const file = await res.json(); console.log(file); dispatch({ image: file.secure_url, largeImage: file.eager[0].secure_url, }) }

我无法使用 UploadFile() 更新图像的 state,不确定我做错了什么。 下面是完整的完整代码。

 import React, { useState, useReducer } from 'react'; import { Mutation } from 'react-apollo'; import gql from 'graphql-tag'; import Router from 'next/router'; import Form from './styles/Form'; import formatMoney from '../lib/formatMoney'; import Error from './ErrorMessage'; const CREATE_LISTING_MUTATION = gql` mutation CREATE_LISTING_MUTATION( $title: String, $description: String, $address: String, $availableFor: String, $spaceType: String, $height: String, $accessType: String, $security: String, $features: String, $nearbyStablishments: String, $rules: String, $image: String, $largeImage: String, $price: Int, $bond: Int, $minBookingStay: Int, $size: String, ) { createListing( title: $title description: $description address: $address availableFor: $availableFor spaceType: $spaceType height: $height accessType: $accessType security: $security features: $features nearbyStablishments: $nearbyStablishments rules: $rules image: $image largeImage: $largeImage price: $price bond: $bond minBookingStay: $minBookingStay size: $size ) { id } } `; const initialState = { title: '', description: '', address: '', availableFor: '', spaceType: '', height: '', accessType: '', security: '', features: '', nearbyStablishments: '', rules: '', image: '', largeImage: '', price: 0, bond: 0, minBookingStay: 0, size: '' }; function reducer(state, {field, value}) { return {...state, [field]: value } } export const CreateListing = () => { const [state, dispatch] = useReducer(reducer,initialState); const handleChange = async (e) => { dispatch({field: e.target.name, value: e.target.value}); } const { title, description, address, availableFor, spaceType, height, accessType, security, features, nearbyStablishments, rules, image, largeImage, price, bond, minBookingStay, size } = state; const uploadFile = async (e) => { console.log('Uploading file...'); const files = e.target.files; const data = new FormData(); data.append('file', files[0]); data.append('upload_preset', 'artemis'); const res = await fetch(`https://api.cloudinary.com/v1_1/${cludinaryAccount}/image/upload`, { method: 'POST', body: data }); const file = await res.json(); console.log(file); dispatch({ image: file.secure_url, largeImage: file.eager[0].secure_url, }) } return ( <Mutation mutation={CREATE_LISTING_MUTATION} variables={state}> { /* 1. Expose createListing function 2. Expose the error and loading state */ } {(createListing, {error, loading, called}) => { // Possible params: error, loading, called, data, info return ( <Form data-test="form" onSubmit={ async (e) => { // Stop the form from submitting e.preventDefault(); // Call the mutation const res = await createListing(); // Change them to the single Listing page console.log(res); Router.push({ pathname: '/listing', query: { id: res.data.createListing.id } }); }}> <h2>Lease my Space</h2> <Error error={error}/> {/* area-busy attribute is needed for our loading animation*/ } <fieldset disabled={loading} aria-busy={loading}> <label htmlFor="file"> Image <input type="file" id = "file" name = "file" placeholder = "Upload an image" required onChange={uploadFile} /> {image && <img src={image} alt="Image Preview"/> } </label> <label htmlFor="title"> Title <input type="text" id = "title" name = "title" placeholder = "title" required value = {title} onChange={handleChange} /> </label> <label htmlFor="description"> Description <input type="text" id = "description" name = "description" placeholder = "Description" required value = {description} onChange={handleChange} /> </label> <label htmlFor="address"> Address <input type="text" id = "address" name = "address" placeholder = "address" required value = {address} onChange={handleChange} /> </label> <label htmlFor="availableFor"> Available For <input type="text" id = "availableFor" name = "availableFor" placeholder = "Available For" required value = {availableFor} onChange={handleChange} /> </label> <label htmlFor="spaceType"> Space Type <input type="text" id = "spaceType" name = "spaceType" placeholder = "Space Type" required value = {spaceType} onChange={handleChange} /> </label> <label htmlFor="height"> Height <input type="text" id = "height" name = "height" placeholder = "Height" required value = {height} onChange={handleChange} /> </label> <label htmlFor="accessType"> Access Type <input type="text" id = "accessType" name = "accessType" placeholder = "Access Type" required value = {accessType} onChange={handleChange} /> </label> <label htmlFor="security"> Security <input type="text" id = "security" name = "security" placeholder = "Security" required value = {security} onChange={handleChange} /> </label> <label htmlFor="features"> Features <input type="text" id = "features" name = "features" placeholder = "Features" required value = {features} onChange={handleChange} /> </label> <label htmlFor="nearbyStablishments"> Nearby Stablishments <input type="text" id = "nearbyStablishments" name = "nearbyStablishments" placeholder = "Nearby Stablishments" required value = {nearbyStablishments} onChange={handleChange} /> </label> <label htmlFor="rules"> Rules <input type="text" id = "rules" name = "rules" placeholder = "Rules" required value = {rules} onChange={handleChange} /> </label> <label htmlFor="image"> Image <input type="text" id = "image" name = "image" placeholder = "Image" required value = {image} onChange={handleChange} /> </label> <label htmlFor="largeImage"> Large Image <input type="text" id = "largeImage" name = "largeImage" placeholder = "Large Image" required value = {largeImage} onChange={handleChange} /> </label> <label htmlFor="price"> Price <input type="number" id = "price" name = "price" placeholder = "Price" required value = {price} onChange={handleChange} /> </label> <label htmlFor="bond"> Bond <input type="number" id = "bond" name = "bond" placeholder = "Bond" required value = {bond} onChange={handleChange} /> </label> <label htmlFor="minBookingStay"> Min Booking stay <input type="number" id = "minBookingStay" name = "minBookingStay" placeholder = "size" required value = {minBookingStay} onChange={handleChange} /> </label> <label htmlFor="size"> size <input type="text" id = "size" name = "size" placeholder = "size" required value = {size} onChange={handleChange} /> </label> <button type="submit"> Submit</button> </fieldset> </Form> ) }} </Mutation> ) } export default CreateListing; export {CREATE_LISTING_MUTATION};

您没有正确使用useReducer API。 您将 reducer 与操作一起使用,以确定应如何根据分派的操作更新 state。 可以在useReducer文档中看到一个基本示例。

您的 reducer 不工作的原因是因为它只能与具有fieldvalue属性的 object 一起工作:

function reducer(state, {field, value}) {
  //                    ^^^^^^^^^^^^^^ needs to be object with field and value props
  return {
    ...state,
    [field]: value
  }
}

您的handleChange function 使用这些属性调度 object。 uploadFile在没有这两个属性的情况下调度 object。 事实上,您当前的 reducer 一次只能更新一个键/值对。 一个快速的解决方法是将uploadFile中的调度更改为:

dispatch({ field: 'image', value: file.secure_url });
dispatch({ field: 'largeImage', value: file.eager[0].secure_url });

这将告诉您的减速器使用这些值更新这些字段。 但这不是useReducer的正确用法。

看起来您正在做的事情可以转换为使用useState ,因为据我从您的减速器 function 了解到,您只是试图将两个对象合并到一个新的 state object 中。React 文档建议将 state 拆分成单独的部分,这通常一起改变,但为了简单起见,我们将坚持使用一个大的 object。

CreateListing中,我们要使用useState

const [state, setState] = useState(initialState);

然后在handleChange中:

const handleChange = (e) => {
  setState((oldState) => ({ ...oldState, [e.target.name]: e.target.value }));
};

同样在uploadFile中:

setState((oldState) => ({
  ...oldState,
  image: file.secure_url,
  largeImage: file.eager[0].secure_url,
}));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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