如何从自定义钩子中获取状态以在组件中更新"parent"?



我正试图将组件中的一些逻辑分离到一个自定义挂钩中。我觉得我误解了一些基本原理,但我认为我的代码可以工作。我基本上是在我的自定义useTrip钩子中更新我的状态,我希望我的map组件也有同样的更新状态。

useTrip.js:

export const useTrip = () => {
const [businesses, setBusinesses] = useState([])
useEffect(()=>{
console.log(businesses) //prints expected results
},[businesses])
const fetchData = async (name, lat, lng) => {
const response = await fetch('http://localhost:5000/category/' + lat + "/" + lng + '/' + name)
const result = await response.json();
setBusinesses(result)
}
return { businesses, fetchData } 
}

Map.js(使用useTrip的组件):

export const Map= (props) => {
const {businesses} = useTrip()
return(<>
{businesses.map((.....)}
</>)
}

Parent.js(map.js的父级):

export const Parent= (props) => {
const {fetchData} = useTrip()
useEffect(() => {
fetchData(title, lat, lng)
}, [origin])
return(<>

</>)
}

当在Map组件内部时,businesses始终是一个空数组。在我开始重构之前,我的代码一直在工作。自定义钩子中的更新状态是否应该在使用它的组件之间保持一致?

您必须在Parent组件上使用自定义挂钩,并通过道具将businesses发送到Map组件

function Parent (props) {
const { fetchData, businesses } = useTrip()
useEffect(() => {
fetchData(title, lat, lng)
}, [origin])
return (
<Map businesses={businesses} />
)
}
function Map (props) {
const { businesses } = props
return (
<>
{businesses.map(/* ... */)}
</>
)
}

如果你在每个组件上调用自定义挂钩,它们将获得自己的状态

我对此进行了一些尝试,并提出了一个更好的解决方案。它在第一个代码块中。

import {useEffect, useState} from 'react';
import { v4 as uuidv4 } from 'uuid';
const constant_data = {
altering_var: null,
queue: {},
default_set: false
};
export const useConstantVariable = (defaultUser) => {
//set an id to a unique value so this component can be identified
const [id, setId] = useState(uuidv4());
//use this variable to force updates to screen
const [updateId, setUpdateId] = useState({});
//set the data contained in this hook
const setData = (data) => {
constant_data.altering_var = data;
};
//force an update of screen
const updateScreen = () => {
setUpdateId({...updateId});
};
//make a copy of the data so it is seen as a new constant instance
const saveData = () =>{
//if the value is an array copy the array
if(Array.isArray(constant_data.altering_var)){
constant_data.altering_var = [...constant_data.altering_var];
//if the value is an object copy it with its prototype
} else if(typeof constant_data.altering_var === 'object' && constant_data.altering_var !== null){
constant_data.altering_var = completeAssign({}, constant_data.altering_var);
} else {
//do no operation on basic types
}
}
//update all instances of this hook application wide
const updateAll = () => {
saveData();

//now get all instances and update them, remove broken links.
Object.keys(constant_data.queue).map((k)=> {
const value = constant_data.queue[k];
if (typeof value !== 'undefined' && value !== null) {
constant_data.queue[k]();
} else {
delete constant_data.queue[k]
}
return true;
});
};
//set the function to call to update this component
constant_data.queue[id] = updateScreen;
//for the first instance of this hook called set the default value.
if (typeof defaultUser !== 'undefined'  && !constant_data.default_set) {
constant_data.default_set = true;
setData(defaultUser);
}
//when this component is destroyed remove all references to it in the queue used for updating.
useEffect(() => {
return () => {
delete constant_data.queue[id];
};
}, []);
//return the new variable to the constant
return [
constant_data.altering_var,
(data) => {
setData(data);
updateAll();
}
];
};

function completeAssign(target, source) {
target = Object.assign(target, source);
Object.setPrototypeOf(target, Object.getPrototypeOf(source));
return target;
}

旧答案

这就是我们设法解决这个问题的方式,它并不完美,我愿意接受改进的建议。但我们创建了一个用户组件,以便在整个应用程序中共享我们的用户。

const users = {client: {isSet: () => { return false; }  } }
const instances = {client: []}

export const useClientUser = (defaultUser) => {

const [updateId, setUpdateId] = useState(uuidv4());
const setClientUser = (data) => {
users.client = new Person(data);
}
const updateScreen = () => {
setUpdateId(uuidv4());
}
useEffect(()=>{
if(defaultUser !== '' && typeof defaultUser !== 'undefined'){
setClientUser(defaultUser);
}
instances.client.push(updateScreen);
}, []);

return [users.client , (data) => { setClientUser(data);
instances.client = instances.client.filter((value)=> {
if(typeof value !== 'undefined'){ return true } else { return false }
} );
instances.client.map((value)=> {if(typeof value !== 'undefined') { value() } })

} ];
}

我已经重写了我们的组件,以显示您的组件将如何假设工作。

import { v4 as uuidv4 } from 'uuid';

//create super globals to share across all components
const global_hooks = {businesses: {isSet: false  } }
const instances = {businesses: []}

export const useTrip = () => {
//use a unique id to set state change of object
const [updateId, setUpdateId] = useState(uuidv4());
//use this function to update the state causing a rerender
const updateScreen = () => {
setUpdateId(uuidv4());
}

//when this component is created add our update function to the update array
useEffect(()=>{
instances.businesses.push(updateScreen);
}, []);
useEffect(()=>{
console.log(global_hooks.businesses) //prints expected results
},[updateId]);
const fetchData = async (name, lat, lng) => {
const response = await fetch('http://localhost:5000/category/' + lat + "/" + lng + '/' + name)
const result = await response.json();
global_hooks.businesses = result;
global_hooks.businesses.isSet = true;
}
return {businesses: global_hooks.businesses, fetchData: (name, lat, lng) => {
//fetch your data
fetchData(name, lat, lng);
//remove update functions that no longer exist
instances.businesses = instances.business.filter((value)=> {
if(typeof value !== 'undefined'){ return true } else { return false }
} );
//call update functions that exist
instances.businesses.map((value)=> {if(typeof value !== 'undefined') { value() } })
}
};
}