#javascript #reactjs #axios
#javascript #reactjs #axios
Вопрос:
Я пытаюсь сделать свой App.js маршрут к my People.jsx и т. Д., Но он работает некорректно. Я надеюсь, что смогу устранить проблему оттуда, если смогу заставить это работать. Я пытался сделать это около 2 часов с помощью правила 20 минут, но с этим мне нужна помощь. Я пробовал другие варианты, но моя цель — theID
передать и Person. Я подумываю об использовании {useContext }
для этого, но я даже не могу заставить его маршрутизировать. Хотел бы я знать, что я делаю неправильно, чтобы я мог это исправить, но другие люди используют разные типы маршрутизаторов, и я был смущен ими еще больше.
Я обновил его ссылками, которые по-прежнему не подходят для меня, какие-либо другие предложения?
App.js
import './App.css';
import { useState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import People from './components/People'
import Planet from './components/Planets'
import Starship from './components/Starships'
import { Router, Link } from '@reach/router';
function App() {
const [starwarsState, setStarwarsState] = useState('')
const [theID, setTheID] = useState('')
const selectedState = (e) => {
setStarwarsState(e.target.value)
}
const switchItem = () => {
switch (starwarsState) {
case 'people':
<Link path='/people/' />;
break;
case 'planets':
<Link path="/planets/" />;
break;
case 'starships':
<Link path='/starships/' />;
break;
default:
return null;
}
}
const addId = e => {
setTheID(e.target.value)
console.log(theID)
}
return (
<div className='App'>
<header className='App-header' >
Search For: amp;nbsp;
<select onChange={selectedState} className='form-control-lg bg-dark text-white'>
<option value='people' active >People</option>
<option value='planets' >Planets</option>
<option value='starships' >Starships</option>
</select>
amp;nbsp; ID: amp;nbsp;
<input type='text' onChange={addId} className='form-control-lg col-sm-1 bg-dark text-white' />amp;nbsp;
<button className='btn-lg btn-warning' onClick={switchItem} >Search Item</button>
<Router>
<People path='/people/' />
<Planet path="/planets/" />
<Starship path='/starships/' />
</Router>
</header>
{starwarsState}
</div>
)
}
export default App;
People.jsx
import React, { useState, useEffect } from 'react'
import axios from 'axios'
import { Link } from '@reach/router';
const People = props => {
const [peopleData, setpeopleData] = useState([]);
useEffect(() => {
axios.get(`https://swapi.dev/api/people/${props.theID}`)
.then(response => { setpeopleData(response.data) })
console.log(peopleData)
}, []);
return (
<div>
<span> the People have spoken</span>
<Link to='/people' />
</div>
)
}
export default People;
Комментарии:
1. Также не могли бы вы описать, когда поправите меня, чтобы я мог учиться.
2. Любой JSX, который вы хотите отобразить, должен быть отображен с помощью возврата функционального компонента или возвращен функцией в JSX. Вы не можете просто вернуть JSX из обратного вызова click и ожидать, что он будет отображен. Почему вы пытаетесь условно отобразить 3 разных маршрутизатора? Просто безоговорочно отобразите один маршрутизатор со всеми тремя маршрутами и условно перейдите к пути маршрута, отображающему компонент, который вы хотите отобразить. Именно на этом этапе вы можете передавать дополнительные данные в маршруты.
3. можете ли вы привести мне пример того, что вы имеете в виду? Я действительно немного смущен тем, что вы подразумеваете под этим
4. Также я хочу, чтобы он передавал информацию, которую я также установил, поэтому, когда я нажимаю кнопку, она переходит на страницу. Вот как я придумал это в своей голове, поэтому я пытаюсь отображать в зависимости от того, что нажимается. Я пытаюсь найти лучший способ сделать это.
5. Можете ли вы показать мне пример? Как будто это не имеет для меня никакого смысла, за исключением того, что я должен сделать что-то вроде
switch(example) People <Link path='/people/'>
Ответ №1:
Проблемы
На самом деле вы не отображаете маршруты / ссылки, switchItem
поскольку onClick
обратные вызовы не могут возвращать визуализируемый пользовательский интерфейс непосредственно в метод визуализации.
Решение
Безоговорочно отображайте все ваши маршруты одновременно в одном Router
и обязательно переходите к ним в switchItem
обработчике.
Приложение
...
import { Router, navigate } from "@reach/router";
...
function App() {
const [starwarsState, setStarwarsState] = useState("");
const [theID, setTheID] = useState("");
...
const switchItem = () => {
switch (starwarsState) {
case "people":
navigate("/people"); // <-- imperative navigation
break;
case "planets":
navigate("/planets");
break;
case "starships":
navigate("/starships");
break;
default:
return null;
}
};
return (
<div className="App">
<header className="App-header">
Search For: amp;nbsp;
<select
onChange={selectedState}
value={starwarsState}
className="form-control-lg bg-dark text-white"
>
<option disabled value="">
Choose Path
</option>
<option value="people">
People
</option>
<option value="planets">Planets</option>
<option value="starships">Starships</option>
</select>
amp;nbsp; ID: amp;nbsp;
<input
type="text"
onChange={addId}
className="form-control-lg col-sm-1 bg-dark text-white"
/>
amp;nbsp;
<button className="btn-lg btn-warning" onClick={switchItem}>
Search Item
</button>
</header>
<Router>
<People path="/people" theID={theID} /> // <-- pass `theID` state as prop
<Planet path="/planets" />
<Starship path='/starships' />
</Router>
</div>
);
}
Люди
const People = ({ theID }) => {
const [peopleData, setpeopleData] = useState([]);
useEffect(() => {
axios.get(`https://swapi.dev/api/people/${theID}`)
.then(response => { setpeopleData(response.data) });
}, [theID]);
return (
<div>
<div>The ID: {theID}</div>
<span>the People have spoken</span>
</div>
);
};
Комментарии:
1. Это здорово, но единственная проблема, с которой я сталкиваюсь, заключается в том, что когда я его использую, если я уже использовал его в People.jsx и пытаюсь изменить человека, мне не нужно нажимать на планеты, а затем возвращаться к людям, если я уже нахожусь на этом маршруте.
2. @ALTHEPAL78 Я не понимаю, что вы имеете в виду под всем этим. Что уже используется в
People.jsx
? Какой человек меняется? Вы имеете вtheID
виду состояние, вApp
котором оно передаетсяPeople
компоненту?3. Я нажимаю на людей и набираю 1, я получаю эту информацию, но если я попытаюсь ввести 2 в поле ID и нажать поиск, это не позволит мне сделать это снова. Состояние изменяется, но оно не будет запускать или отправлять новый запрос в API
4. @ALTHEPAL78 Ах, я понимаю. Это связано с тем, что эффект имеет пустой массив зависимостей и запускается только один раз при монтировании. Вы можете просто добавить
theID
в массив зависимостей, если хотите, чтобы эффект снова запускался приtheID
обновлении в родительском. Я обновил код в ответе и в связанном codesandbox.
Ответ №2:
Используйте императивную маршрутизацию (не оператор switch) с обработчиками событий
В вашем коде используется оператор switch в сочетании с функцией switchItem() . Это не то, как перенаправить пользователя в обязательном порядке (то есть через что-то другое, кроме ссылки, на которую пользователь нажимает непосредственно).
Чтобы принудительно маршрутизировать своих пользователей, используйте navigate
метод.
Через документы Reach Router (ссылка):
Иногда вам нужно перемещаться в ответ на что-то другое, а не на то, что пользователь нажимает на ссылку. Для этого у нас есть навигация. Давайте импортируем navigate.
import {
Router,
Link,
navigate
} from "@reach/router"
В вашем случае весь оператор switch можно переписать следующим образом:
useEffect(() => navigate(`/${starwarsState}`), [starwarsState])
useEffect будет следить за изменениями в starwarsState, которые либо будут «людьми», «планетами», либо «звездолетами». Как только произойдет изменение, оно перенаправит пользователя на соответствующий путь.
Решение: только маршрутизация
Следующее решение не реализует axios, оно фокусируется исключительно на логике маршрутизации на стороне клиента.
Я обнаружил некоторые другие проблемы с вашим кодом, когда работал над решением. Вот версия, которую я написал, которая реализует маршрутизацию на уровне параметров, а также выполняет некоторую другую очистку (рефакторинг категорий swapi в объект конфигурации и т. Д.).
App.js
import React, { useState, useEffect } from 'react'
import { useWhatChanged } from "@simbathesailor/use-what-changed";
import { Router, Link, navigate } from "@reach/router";
import 'bootstrap/dist/css/bootstrap.min.css';
import { People, Person } from './components/People'
import { Planets, Planet } from './components/Planets'
import { Starships, Starship } from './components/Starships'
import './App.css';
function App() {
// destructure categories from config
const { people, planets, starships } = config.categories
// initialize state
const [starwarsState, setStarwarsState] = useState(people);
const [theID, setTheID] = useState('');
// log updates to ID and starwarsState
useWhatChanged([starwarsState, theID], 'starwarsState, theID')
// change state on input from user
const addId = e => setTheID(e.target.value)
const selectCategory = (e) => setStarwarsState(e.target.value)
// route the user based on starwarsState
useEffect(() => navigate(`/${starwarsState}`), [starwarsState])
// search swapi based on category and id
const searchSwapi = e => {
e.preventDefault()
navigate(`/${starwarsState}/${theID}`)
}
return (
<div className="App">
<header className='App-header' >
Search For:
<form onSubmit={searchSwapi}>
<select onChange={selectCategory} className='form-control-lg bg-dark text-white'>
<option value={people} >People</option>
<option value={planets} >Planets</option>
<option value={starships} >Starships</option>
</select>
ID:
<input type='text' onChange={addId} className='form-control-lg col-sm-1 bg-dark text-white' />
<button className='btn-lg btn-warning' >Search Item</button>
</form>
</header>
<Router>
<People path='/people/'>
<Person path=':personId' />
</People>
<Planets path="/planets/">
<Planet path=':planetId' />
</Planets>
<Starships path='/starships/'>
<Starship path=':starshipId' />
</Starships>
</Router>
</div>
)
}
const config = {
categories: {
people: 'people',
planets: 'planets',
starships: 'starships'
}
}
export default App;
Planets.js
import React from 'react'
export const Planets = props => {
return (
<div>
<span> the Planets have spoken</span>
{props.children}
</div>
)
}
export const Planet = props => {
return (
<div>
Planet Data
</div>
)
}
People.js
import React, { useState, useEffect } from 'react'
export const People = props => {
return (
<div>
<span> the People have spoken</span>
{props.children}
</div>
)
}
export const Person = props => {
return (
<div>
Person Data
</div>
)
}
Starships.js
import React from 'react'
export const Starships = props => {
return (
<div>
<span> the Starships have spoken</span>
{props.children}
</div>
)
}
export const Starship = props => {
return (
<div>
Starship Data
</div>
)
}
[ОБНОВЛЕНИЕ] Решение: маршрутизация с помощью вызовов API
Следующее решение берет код сверху и реорганизует его, используя шаблон управления состоянием, предложенный Ли Холлидеем. Решение добавляет три вещи:
- useContext для управления глобальным состоянием
- React.memo() для запоминания компонента AppContent
- react-запрос для управления удаленными вызовами API для SWAPI.
App.js
// App.js
import React, {
useState,
useEffect,
createContext,
useContext,
memo
} from 'react'
import { ReactQueryDevtools } from "react-query-devtools";
import { useWhatChanged } from "@simbathesailor/use-what-changed";
import { Router, navigate } from "@reach/router";
import 'bootstrap/dist/css/bootstrap.min.css';
import { People, Person } from './components/People'
import { Planets, Planet } from './components/Planets'
import { Starships, Starship } from './components/Starships'
import './App.css';
import Axios from 'axios';
// APP w/ CONTEXT PROVIDER
export default function App() {
return (
<>
<StarwarsProvider>
<AppContent />
</StarwarsProvider>
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
// CREATE CONTEXT
export const StarwarsContext = createContext()
// CONTEXT PROVIDER
function StarwarsProvider({ children }) {
// import categories
const categories = config.categories
// destructure default category of search selection
const { people } = categories
// initialize state
const [category, setCategory] = useState(people);
const [theID, setTheID] = useState('');
return (
<StarwarsContext.Provider value={{
category,
setCategory,
theID,
setTheID,
categories,
fetchStarwarsData
}}>
<AppContent />
</StarwarsContext.Provider>
)
}
async function fetchStarwarsData(category, id) {
if (!id) {
return
}
const response = await Axios.get(
`https://swapi.dev/api/${category}/${id}`
).then(res => res.data)
// const data = await response.json()
const data = response
// console.log(data)
return data
}
// APP CONTENT
const AppContent = memo(() => {
// import global state into component
const { category, setCategory } = useContext(StarwarsContext)
const { theID, setTheID } = useContext(StarwarsContext)
// destructure categories
const { categories: { people, planets, starships } } = useContext(StarwarsContext)
// log updates to ID and category
useWhatChanged([category, theID], 'category, theID')
// change state on input from user
const addId = e => setTheID(e.target.value)
const selectCategory = (e) => setCategory(e.target.value)
// route the user based on category
useEffect(() => navigate(`/${category}`), [category])
// search swapi based on category and id
const searchSwapi = e => {
e.preventDefault()
navigate(`/${category}/${theID}`)
}
return (
<div className="App">
<header className='App-header' >
Search For:
<form onSubmit={searchSwapi}>
<select onChange={selectCategory} className='form-control-lg bg-dark text-white'>
<option value={people} >People</option>
<option value={planets} >Planets</option>
<option value={starships} >Starships</option>
</select>
ID:
<input type='text' onChange={addId} className='form-control-lg col-sm-1 bg-dark text-white' />
<button className='btn-lg btn-warning' >Search Item</button>
</form>
</header>
<Router>
<People path='/people/'>
<Person path=':personId' fetchStarwarsData />
</People>
<Planets path="/planets/">
<Planet path=':planetId' fetchStarwarsData />
</Planets>
<Starships path='/starships/'>
<Starship path=':starshipId' fetchStarwarsData />
</Starships>
</Router>
</div>
)
})
const config = {
categories: {
people: 'people',
planets: 'planets',
starships: 'starships'
}
}
People.js
// People.js
import React from 'react'
import { useQuery } from "react-query";
import { StarwarsContext, StarwarsProvider } from "../App"
export const People = props => {
return (
<div>
<span> the People have spoken</span>
{props.children}
</div>
)
}
export const Person = () => {
const { category, theID, fetchStarwarsData } = React.useContext(StarwarsContext)
const { data, isLoading, error } = useQuery([category, theID], fetchStarwarsData)
if (isLoading) return <div>loading...</div>
if (error) return <div>oop!! error ocurred</div>
return (
<div>
<h1>/{category}/{theID}</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)
}
Planets.js
// Planets.js
import React from 'react'
import { useQuery } from "react-query";
import { StarwarsContext, StarwarsProvider } from "../App"
export const Planets = props => {
return (
<div>
<span> the Planets have spoken</span>
{props.children}
</div>
)
}
export const Planet = props => {
const { category, theID, fetchStarwarsData } = React.useContext(StarwarsContext)
const { data, isLoading, error } = useQuery([category, theID], fetchStarwarsData)
if (isLoading) return <div>loading...</div>
if (error) return <div>oop!! error ocurred</div>
return (
<div>
<h1>/{category}/{theID}</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)
}
Starships.js
// Starships.js
import React from 'react'
import { useQuery } from "react-query";
import { StarwarsContext, StarwarsProvider } from "../App"
export const Starships = props => {
return (
<div>
<span> the Starships have spoken</span>
{props.children}
</div>
)
}
export const Starship = () => {
const { category, theID, fetchStarwarsData } = React.useContext(StarwarsContext)
const { data, isLoading, error } = useQuery([category, theID], fetchStarwarsData)
if (isLoading) return <div>loading...</div>
if (error) return <div>oop!! error ocurred</div>
return (
<div>
<h1>/{category}/{theID}</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)
}
Комментарии:
1. Я попробовал это, но тогда как мне извлечь данные с сайта api, которого больше нет в этой версии
2. Я тоже работал над этим прошлой ночью, но столкнулся с некоторыми проблемами, поэтому решил сначала сосредоточиться на логике переключения. Позже я еще раз взгляну на код, чтобы подумать о том, как реализовать вызовы API с использованием этой архитектуры.
3. @ALTHEPAL78 Сначала посмотрите это видео об управлении состоянием: ссылка . Я взял тот же шаблон и использовал его, чтобы заставить мое решение работать с SWAPI. Полное решение опубликовано здесь и на GitHub ( ссылка ). Если это решение работает и для вас, пожалуйста, подумайте о том, чтобы пометить его как правильный ответ.
4. Это выглядит потрясающе, но теперь мне нужен класс во всех разных @ things, которые вы используете. Я пытаюсь предпринять к этому небольшие шаги. Это сбивает меня с толку, потому что я только видел, но не использовал, многие из тех импортных файлов, которые вы импортируете, поэтому я пытаюсь сначала разобраться с основами.
5. @ALTHEPAL78 Это действительно отличный отзыв, и я полностью понимаю, к чему вы клоните. Позвольте мне посмотреть, смогу ли я разбить тот же код, используя меньше сторонних библиотек, чтобы его было легче понять.