#reactjs #typescript #react-hooks #fetch
Вопрос:
В моем родительском компоненте, Dashboard.tsx, у меня есть дочерний компонент, Expenses.tsx, который выполняет вызов выборки API, а затем отображает данные. Родительский компонент имеет маршрутизатор, который позволяет вам переходить по разным URL-адресам в родительском компоненте, что заставляет все перерисовываться каждый раз, когда вы переходите по новому пути или отображаете новый дочерний компонент. Как я могу сделать так, чтобы этот вызов выборки выполнялся только один раз? Я пробовал использовать крючок useRef (), но он повторно инициализируется каждый раз при повторном рендеринге, и у меня та же проблема.
Вот панель мониторинга.tsx:
export const Dashboard = () => {
const d = new Date()
const history = useHistory()
const { user, setUser } = useAuth()
const [categories, setCategories] = useState({
expenseCategories: [],
incomeCategories: []
})
const getCategories = async(user_id: number) => {
await fetch(`/api/getCategories?user_id=${user_id}`)
.then(result => result.json())
.then(result => setCategories(result))
}
useEffect(() => {
if (user.info.user_id) {
getCategories(user.info.user_id)
}
}, [])
const dashboardItems = [
{
value: 'Add Expense',
path: '/dashboard/addExpense'
},
{
value: 'Add Income',
path: '/dashboard/addIncome'
},
{
value: 'Logout',
path: '/login',
onClick : async() => {
localStorage.clear()
setUser({
info: {
user_id: null,
email: null,
username: null
},
token: null
})
},
float: 'ml-auto'
}
]
return(
<div>
<DashboardNavbar items={dashboardItems}/>
<div className="wrapper">
<p>{`Hello, ${user.info.username}!`}</p>
<DateAndTime />
<Expenses date={d}/>
<Income date={d}/>
<Switch>
<Route path='/dashboard/addExpense'>
<AddItemForm user={user} type={'expenses'} categories={categories.expenseCategories} />
</Route>
<Route path='/dashboard/addIncome'>
<AddItemForm user={user} type={'income'} categories={categories.incomeCategories} />
</Route>
</Switch>
<Logout />
</div>
</div>
)
}
А вот файл Expenses.tsx, где выполняется вызов выборки:
export const Expenses = (props: ExpensesProps) => {
const [isLoading, setIsLoading] = useState(true)
const { date } = props
const { user } = useAuth()
const m = date.getMonth() 1
const s = '0'.concat(m.toString())
const [total, setTotal] = useState<number>(0)
useEffect(() => {
const getTotalExpenses = async() => {
await fetch(`/api/expenses?user_id=${user.info.user_id}amp;month=${s}`)
.then(response => response.json())
.then(result => {
if (result) {
setTotal(parseFloat(result))
}
})
.then(result => {
setIsLoading(false)
})
}
if (user.info.user_id) {
getTotalExpenses()
}
}, [])
return isLoading ? (
<div>
loading...
</div>
) : (
<div>
{`Your monthly expenses so far are: ${total}.`}
</div>
)
}
Комментарии:
1. Каждый экземпляр
Expenses
отличается, поэтому, если вы хотите, чтобы новый экземпляр получал доступ к данным из предыдущего экземпляра, вам нужно хранить эти данные выше в цепочке. Вы можете хранить егоDashboard
и передавать реквизит, напримерtotal
,setTotal
и. Или вы могли бы использовать что-то вроде Redux, которое использует контексты реакции.2. Ах, в этом есть смысл. Хорошо — если я все же сохраню его
Dashboard
, не будет ли он перерисовываться каждый разDashboard
при повторных отправках, всякий раз, когда я перехожу на новый маршрут? Тогда он в конечном итоге будет демонстрировать то же поведение (повторная выборка).3. Вам нужно будет добавить какую-то условную проверку, чтобы вы могли получать данные только в том случае, если у вас еще нет данных.
4. правильно — чего я не понимаю, так это как или где хранить эти данные после первоначальной выборки, чтобы я не извлекал их снова. У меня возникли проблемы с тем, чтобы заставить пользователя сделать это — как бы вы порекомендовали ?