API 在 React inside map 中调用 render 方法
API call in render method in React inside map
我有一个 userId
数组,我需要显示与该数组相关的名称列表。我想在 render
方法中调用 API 并获取用户名。但这是行不通的。我该如何解决这个问题?
下面是我的render
方法:
render(){
...
return(
<div>
{this.state.users.map(userId => {
return (
<div> {this.renderName(userId )} </div>
)
})}
</div>
)
...
}
下面是 renderName
函数:
renderName = (userId) => {
axios.get(backendURI.url + '/users/getUserName/' + userId)
.then(res => <div>{res.data.name}</div>)
}
通常,您不会直接在渲染方法中更改状态或获取数据。状态总是由 actions/events(点击、输入或其他)改变。每次 prop/state 更改时都会调用 render 方法。如果直接在 render 方法中更改状态,最终会出现无限循环。
您应该使用生命周期方法或挂钩从 api 加载数据。这是官方 React FAQ 中的示例:https://reactjs.org/docs/faq-ajax.html
基本上你不能在渲染中使用异步调用,因为它们 return 一个 Promise,它不是有效的 JSX。而是使用 componentDidMount 和 setState 用他们的名字更新用户数组。
这不会呈现任何东西,因为 API 调用是异步的,并且由于 renderName
函数没有 return 任何东西,它会 return 未定义。
您应该创建一个函数,它将为所有用户 ID 调用 api 并更新状态
getNames = () => {
const promises = [];
this.state.users.forEach((userId) => {
promises.push(axios.get(backendURI.url+'/users/getUserName/'+userId));
})
// Once all promises are resolved, update the state
Promise.all(promises).then((responses) => {
const names = responses.map((response) => response.data.names);
this.setState({names});
})
}
现在只要 users
数据可用,您就可以在 componentDidMount
或 componentDidUpdate
中调用此函数。
最后,您可以直接遍历名称并呈现它们
<div>
{this.state.names.map((name) => {
return <div> {name} </div>;
})}
</div>
您可以将用户名设为自己的组件:
const request = (id) =>
new Promise((resolve) =>
setTimeout(resolve(`id is:${id}`), 2000)
);
const UserName = React.memo(function User({ userId }) {
const [name, setName] = React.useState('');
React.useEffect(() => {
//make the request and set local state to the result
request(userId).then((result) => setName(result));
}, [userId]);
return <div> {name} </div>;
});
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [1, 2],
};
}
render() {
return (
<ul>
{this.state.users.map((userId) => (
<UserName key={userId} userId={userId} />
))}
</ul>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
export default ()=> {
let [users,setUsers] = useState([]);
useEffect(()=>{
let fetchUsersInfoRemote = Promise.all([...Array(10)].map(async (_,index)=>{
try {
let response = await axios.get(`https://jsonplaceholder.typicode.com/posts/${index+1}`);
return response.data;
}
catch(error) {
return ;
}
}));
fetchUsersInfoRemote.then(data=> setUsers(data));
},[]);
return (
<div className="App">
<ul>
{
users.map(user=>(<li><pre>{JSON.stringify(user,null,2)}</pre></li>))
}
</ul>
</div>
);
}
我有一个 userId
数组,我需要显示与该数组相关的名称列表。我想在 render
方法中调用 API 并获取用户名。但这是行不通的。我该如何解决这个问题?
下面是我的render
方法:
render(){
...
return(
<div>
{this.state.users.map(userId => {
return (
<div> {this.renderName(userId )} </div>
)
})}
</div>
)
...
}
下面是 renderName
函数:
renderName = (userId) => {
axios.get(backendURI.url + '/users/getUserName/' + userId)
.then(res => <div>{res.data.name}</div>)
}
通常,您不会直接在渲染方法中更改状态或获取数据。状态总是由 actions/events(点击、输入或其他)改变。每次 prop/state 更改时都会调用 render 方法。如果直接在 render 方法中更改状态,最终会出现无限循环。
您应该使用生命周期方法或挂钩从 api 加载数据。这是官方 React FAQ 中的示例:https://reactjs.org/docs/faq-ajax.html
基本上你不能在渲染中使用异步调用,因为它们 return 一个 Promise,它不是有效的 JSX。而是使用 componentDidMount 和 setState 用他们的名字更新用户数组。
这不会呈现任何东西,因为 API 调用是异步的,并且由于 renderName
函数没有 return 任何东西,它会 return 未定义。
您应该创建一个函数,它将为所有用户 ID 调用 api 并更新状态
getNames = () => {
const promises = [];
this.state.users.forEach((userId) => {
promises.push(axios.get(backendURI.url+'/users/getUserName/'+userId));
})
// Once all promises are resolved, update the state
Promise.all(promises).then((responses) => {
const names = responses.map((response) => response.data.names);
this.setState({names});
})
}
现在只要 users
数据可用,您就可以在 componentDidMount
或 componentDidUpdate
中调用此函数。
最后,您可以直接遍历名称并呈现它们
<div>
{this.state.names.map((name) => {
return <div> {name} </div>;
})}
</div>
您可以将用户名设为自己的组件:
const request = (id) =>
new Promise((resolve) =>
setTimeout(resolve(`id is:${id}`), 2000)
);
const UserName = React.memo(function User({ userId }) {
const [name, setName] = React.useState('');
React.useEffect(() => {
//make the request and set local state to the result
request(userId).then((result) => setName(result));
}, [userId]);
return <div> {name} </div>;
});
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [1, 2],
};
}
render() {
return (
<ul>
{this.state.users.map((userId) => (
<UserName key={userId} userId={userId} />
))}
</ul>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
export default ()=> {
let [users,setUsers] = useState([]);
useEffect(()=>{
let fetchUsersInfoRemote = Promise.all([...Array(10)].map(async (_,index)=>{
try {
let response = await axios.get(`https://jsonplaceholder.typicode.com/posts/${index+1}`);
return response.data;
}
catch(error) {
return ;
}
}));
fetchUsersInfoRemote.then(data=> setUsers(data));
},[]);
return (
<div className="App">
<ul>
{
users.map(user=>(<li><pre>{JSON.stringify(user,null,2)}</pre></li>))
}
</ul>
</div>
);
}