我想在两个兄弟之间传递数据,但是出于学习目的,我不想使用上下文。我有搜索栏自动选择搜索城市:
SearchBar.js
export const SearchBar = () => {
const [city, setCity] = useState('')
const [error, setError] = useState(null)
const [loading, setLoading] = useState(true)
const [cityList, setCityList] = useState(null)
const handleOnSearch = (string, cityList) => {
if(string.length >= 3) {
const options = {
method: `GET`,
};
fetch(`/api/cityList?city=${string}`, options)
.then((response) => {
if(response.ok){
return response.json().then(setCityList)
}
throw new Error('Api is not available')
})
.catch(error => {
console.error('Error fetching data: ', error)
setError(error)
})
.finally(setLoading(false))
}
}
const allCities = cityList ? cityList : []
const handleOnSelect = (item) => {
const city = item?.name
setCity(city)
}
return(
<div className="mb-6">
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Search city
</label>
<div className="grid grid-cols-5 gap-4">
<div className="col-span-3">
<div className="mt-1">
<ReactSearchAutocomplete
id="city"
name="city"
type="text"
items={allCities}
fuseOptions={{ keys: ["name"] }}
onSearch={handleOnSearch}
onSelect={handleOnSelect}
autoFocus
autoComplete="off"
className="py-3 px-4 block w-full shadow-sm focus:ring-blue-500 focus:border-blue-500 border-gray-300 rounded-md"
/>
</div>
</div>
<div className="col-span-2 mt-1">
<button className="mt-1 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" type='button' onClick={handleOnSelect}>
Select City
</button>
</div>
</div>
</div>
)
}
在同一个索引页上,我有另一个组件,应该从SearchBar.js中获取城市,并对其进行进一步的操作:
ForecastButtons.js
export const ForecastButtons = () => {
const [payload, setPayload] = useState(null)
const fetchCityData = () => {
const options = {
method: `POST`,
};
fetch(`/api/weather?city=${city}`, options)
.then((response) => {
if(response.ok){
return response.json().then(setPayload)
}
throw new Error('Api is not available')
})
.catch(error => {
console.error('Error fetching data: ', error)
setError(error)
})
.finally(setLoading(false))
}
const location = payload?.location?.name;
const currentTemp = payload?.current?.temp_c;
return(
<div className="sm:col-span-2">
<p className="block text-sm font-medium text-gray-700">Select forecast</p>
<button onSubmit={fetchCityData} className="mt-1 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded">
Today
</button>
<p key={city?.location?.id} className='my-5'>
{ location ? `Current weather in ${location} is ${currentTemp} degrees ` : 'Please search for city to see current weather'}
</p>
</div>
)
}
我有点知道如何在上下文中做到这一点,但我现在正在学习如何反应,所以,我想去一个更"复杂"的。在组件之间传递数据的方式,不需要上下文。
这些组件不(也不应该)知道彼此的任何信息。从使用它们的父组件的角度来看,它们是"兄弟"组件。从某种意义上说,它们都存在于彼此相邻的标记中。但是它们不一定非要是兄弟姐妹。这不应该是使用组件的必要条件。
如果这些组件需要依赖传递给它们的任何类型的外部状态,那么您可以将状态提升到父组件。基本上,不是试图让兄弟节点相互通信,而是每个节点应该简单地将其所需的信息作为道具公开。然后由承载该信息的组件负责提供该信息。
所以在这种情况下,包含组件将跟踪city
的状态:
const [city, setCity] = useState('');
SearchBar
组件需要传递给它的这两个东西:
<SearchBar city={city} setCity={setCity} />
它会在内部使用它们而不是跟踪状态本身:
export const SearchBar = ({city, setCity}) => {
const [error, setError] = useState(null)
const [loading, setLoading] = useState(true)
const [cityList, setCityList] = useState(null)
// the rest of the logic is the same...
};
ForecastButtons
分量只需要知道city
:
<ForecastButtons city={city} />
作为道具使用:
export const ForecastButtons = ({city}) => {
const [payload, setPayload] = useState(null)
// the rest of the component...
};
这个想法是在更高级别的组件中跟踪状态,以便它可以被子组件共享。
随着应用程序的增长,这当然会变得相当复杂。在一些没有使用有效状态管理的代码库中,您可能会发现组件从更高的级别通过它们传递状态值,以便较低级别的组件可以使用该状态。这就是useContext
要解决的问题。或者,你可以使用像Redux
这样的状态管理库来全局管理状态,每个组件可以只读取它需要的状态。
但无论如何,任何时候两个组件需要共享相同的状态,该状态都会从这些组件重构到更高的级别(父组件、上下文、全局状态管理器),以便这些组件共享。
最简单的方法是移动状态,允许将城市更改为包含ForceastButtons
和SearchBar
组件的索引组件。
const [city, setCity] = useState('')
除此之外,必须将setCity
方法传递给SearchBar
,并将city state传递给两个子组件。