i have a JSON as a server, the JSON have a 'recipes' array which have objects into it
"recipes": [
{
"id": "1",
"title": "Arepa",
"description": "...",
"image": "...",
"preparation": "...",
"ingredients": "..",
"notes": "..."
},
{
"id": "2",
"title": "Burritos",
"description": "...",
"image": "...",
"preparation": "...",
"ingredients": "...",
"notes": "..."
}
Iḿ trying to display the images into a slider with a loop,but the only thing im getting is errors and frustration,i cant get data easily but display it get me errors, I've tried so many thing and nothing work. this is my code.
TopTen.js
import { CarouselProvider, Slider, ButtonBack, ButtonNext } from 'pure-react-carousel';
const Topten = () => {
return (
<div id="topTenContainer">
<CarouselProvider
naturalSlideWidth={100}
naturalSlideHeight={125}
totalSlides={3}
>
<Slider>
//// here must be the images ////
</Slider>
<ButtonBack>Back</ButtonBack>
<ButtonNext>Next</ButtonNext>
</CarouselProvider>
</div>
);
};
export default Topten;
when i tried create a async function and tried put into i have this error => Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.here the example
import database from "../api/database"
const Sliderr = async() =>{
const responde = await database.get('recipes');
const recipes = responde.data;
return(
{
recipes.map(post =>{
return(
<div key={post.id}>
<h1>{post.title}</h1>
</div>
)
})
}
)
}
export default Sliderr
any help please?
Can you print "recipe" and "responde" values in the console? if you can print it, try pulling the constants of the specified variables from "const" to "var".First of all, make sure of the returned answer. Perform step by step operations.
recipes?.map(post =>{
return(
<div key={post?.id}>
<h1>{post?.title}</h1>
</div>
)
})
you have to check if data has title field render the h1 component with using mark question
Well, the main problem is in fact as stated in the log:
Your Sliderr Component should return a NON async function. The function/functional-component "Sliderr" returns a Promise when you use it inside the jsx block in TopTen.js as it is declared as an async function.
You could handle the async aspect inside a useEffect e. g.
const Topten = () => {
const [data, setData] = useState([]);
useEffect(() => {
const getPosts = async () => setData(await database.get('recipes'));
getPosts();
}, []);
return (
<div id="topTenContainer">
<CarouselProvider
naturalSlideWidth={100}
naturalSlideHeight={125}
totalSlides={data.length}
>
<Slider>
{data.map(post => <Slide>{post.title}</Slide>)}
</Slider>
<ButtonBack>Back</ButtonBack>
<ButtonNext>Next</ButtonNext>
</CarouselProvider>
</div>
);
};
You still would need to refactor your Sliderr Component into Slide though taking post.title as the children-prop.
Building on that you could think about extracting the api call into a custom hook.
[Edit: Sorry about my optimistic approach. I left out the error handling for the request]
Related
I'm tying to find a way to correct the Hook rendering error. I have a total of 3 useQuery hooks being rendered :
const {
data: OSData,
error: OSError,
loading: OSLoading,
} = useQuery(OSData, {
variables: {
NUMBER: UniqueList,
},
})
const {
data: RamData,
error: RamERROR,
loading: RamLOADING,
} = useQuery(GET_Ram)
const {
data: Hardware,
error: HardwareERROR,
loading: HardwareLOADING,
} = useQuery(GET_Hardware)
The variable 'NUMBER' is based on a list 'UniqueList' that is made from the GET_Ram and GET_Hardware queries so the OSData query needs to be called later or there's an undefined variable. However, calling the OSData Query later in the code gives me a render error.
Any idea on how I could accomplish this?
Thank you!
an answer I found is using lazy query.
const SomeData [{
called, loading, data
}] = useLazyQuery(OSData)
})
if (called && loading) return <p>Loading ...</p>
if (HardwareLOADING || RamLOADING) return <p> loading</p>
if (HardwareERROR || RamERROR) return <p>error</p>
//perform all the needed calculations for the variable here
and in the return statement you can call the query and provide the variable. Here I use a button.
<div>
<button onClick={() => SomeData({ variables: { NUMBER: uniqueList } })}>
Load{' '}
</button>
</div>
Hope this helps someone
When navigating from a link in the same web app to the dynamically routed page clicking on a link the result is as intended: I navigate to the page for a product (http://localhost/1).
But when I directly navigate by naming the product number specifically in the search bar (navigating to http://localhost/2), I get the following error:
Server Error
TypeError: Cannot read property 'image' of undefined
> | <Image src={"/../public/images/" + p.image}
^
So far I've tried making the types match and reading the Next JS docs on dynamically routing.
I've removed the array zero from the filter but still no resolution.
Could it be possible that the routing only works when clicking on a link in Next JS? Is there some missing setting I've neglected?
pages/[pid].js
import { useRouter } from 'next/router'
import Image from 'next/image'
import data from '../products.json'
export default function Template() {
const router = useRouter()
const { pid } = router.query
const p = data.filter(product => product._id == pid)[0] // Choose one result
return (
<Image src={"/../public/images/" + p.image}
height="500px"
width="500px" />
)
}
products.json
[
{
"_id": 1,
"name": "Toyota",
"image": "toyota.png"
},
{
"_id": 2,
"name": "BMW",
"image": "bmw.png"
}
]
Update: I've tried to hardcode the src attribute in the Image tag and the new error says the other references are the issue. So I can safely say the issue is to do with no object returned when the data object is called.
I solved the issue!
It was not enough to use Dynamic Routes by using the 'useRouter()' function. I also had to define these two functions:
export async function getStaticProps(context) {
// No-op since getStaticPaths needs getStaticProps to be called.
return { props: {} }
}
export async function getStaticPaths() {
const dynamicFiles = products.map(product => (
{
params: { pid: String(product._id) }, // Product IDs are ints in JSON file
}
))
return {
paths: dynamicFiles,
fallback: false
}
}
This makes sense since you wouldn't want random paths to be used as a variable. For example, then a user would be able to specify http://localhost/1234 when 1234 is not a valid option.
https://nextjs.org/learn/basics/dynamic-routes/implement-getstaticprops
I just got started to react so please bear with me. I don't know exactly what I am doing, I'm just picking those things as I go so I'll do my best to walk you through my mental process when building this.
My intentions are to create a registration component, where the backend returns the validation errors in case there are any in form of an object which has following structure.
{
"username": [
"A user with that username already exists."
],
"email": [
"A user is already registered with this e-mail address."
]
}
The state manager that I chose to be using is redux, so this comes back every time when the register function is dispatched.
Since it has this structure I wrote a function to help me decompose it and pick up only on the actual errors (the strings).
const walkNestedObject = (obj, fn) => {
const values = Object.values(obj)
values.forEach(val =>
val && typeof val === "object" ? walkNestedObject(val, fn) : fn(val))
}
now I want to display them in the view, so I wrote another function which is supposed to do that
const writeError = (value) => {
return <Alert message={value} type="error" showIcon />
}
Down in the actual component I am calling it as this:
{(props.error) ? walkNestedObject(props.error, writeError) : null}
To my surprise if I console.log the value above return in writeError it works flawlessly, every single error gets printed, but none of them gets rendered.
To debug this I've tried multiple variations and none of them seemed to work, I even called the writeError function in the component as
{writeError('test')}
and it worked for some reason.
At this stage I'm just assuming there's some react knowledge required to fulfil this task that Im just now aware of.
EDIT:
A mock example can be found over here
Also, I've tried using the first two answers and when mapping through the errors I get this
Unhandled Rejection (TypeError): props.error.map is not a function
with other variations, it mentions the promise from so I'd include how I manage the API request
export const authSignup = (username, email, password1, password2) => dispatch => {
dispatch(authStart());
axios.post('http://127.0.0.1:8000/rest-auth/registration/', {
username: username,
email: email,
password1: password1,
password2: password2
})
.then(res => {
const token = res.data.key;
const expirationDate = new Date(new Date().getTime() + 3600 * 1000);
localStorage.setItem('token', token);
localStorage.setItem('expirationDate', expirationDate);
dispatch(authSuccess(token));
dispatch(checkAuthTimeout(3600));
})
.catch(err => {
dispatch(authFail(err.response.data))
})
}
Consider changing the topology of your error messages:
"errors": [
{ "type": "username", "message": "Username already in use." },
{ "type": "email", "message": "Email address already in use."}
]
That makes your implementation a bit easier:
// MyLogin.jsx
import React from 'react'
const MyLogin = () => {
/**
* Here we're using state hooks, since it's much simpler than using Redux.
* Since we don't need this data to be made globally available in our
* application, it doesn't make sense to use Redux anyway.
*/
const [errors, setErrors] = React.useState([])
const handleLogin = (event) => {
event.preventDefault()
axios.post('/api/login', formData).then(() => successAction(), (error: any) => {
setErrors(error) // Update our local state with the server errors
})
}
return (
<>
{errors.length && ( // Conditionally render our errors
errors.map((error) => (
<Alert type={error.type} message={error.message} />
)
)}
<form onSubmit={handleLogin}>
<input type='text' name='email' />
<input type='text' name='username' />
<input type='password' name='password' />
</form>
<>
)
}
export default MyLogin
Your walkNestedFunction function checks each layer of an object, and if a given layer of the object is an object itself, it then uses that object to run your function - which in this case is writeError. writeError returns an error <Alert /> as soon as an error arises. But when you stick writeError inside the circular logic of walkNestedFunction, it will hit the first return statement, render it to the page, and then stop rendering. I think this is why you're getting the complete list of errors logged to the console. Your walkNestedFunction continues cycling down through object layers until its done. But in React, only the first return statement will actually render.
A better tactic would be to modify your writeError function to record the erors to a state variable. Then you can render this state variable. Every time the state is updated, the component will rerender with the updated state.
// Define state in your component to contain an array of errors:
state = {
errors: []
}
// Add an error into state with each iteration of writeError
const writeError = (error) => {
this.setState({
errors: [
...this.state.errors,
error
]
})
}
// inside render(), render the state variable containing your errors
<div>
{ this.state.errors.map(error => <p>error</p>) }
</div>
`
Fairly new to OIDC - I've done a bit of work with it in the past, but I wouldn't call myself an expert by any means.
I am trying to use OIDC in a react app using the oidc-client-js and redux-oidc libraries (along with the redux-oidc-example as an example)
I'm getting this error:
Error: No matching state found in storage
at eval (oidc-client.min.js:1011)
Now I've looked around for a solution to this, and this is the closest I'm seeing to my problem, but I still don't find anything here clearly explaining anything:
https://github.com/IdentityModel/oidc-client-js/issues/648
Here's my callback function:
const successCallback = (user) => {
this.props.dispatch(push("/settings"))
};
class CallbackPage extends React.Component {
render() {
return (
<CallbackComponent
userManager={userManager}
successCallback={successCallback}
errorCallback={error => {
//this.props.dispatch(push("/chat"));
console.error(error);
}}
>
<div>Redirecting TEST...</div>
</CallbackComponent>
);
}
}
export default connect()(CallbackPage);
Before commenting out the props.dispatch in the error callback, it was a never-ending loop. Now it just freezes on the callback page with whatever I put in the error callback.
I won't put my full userManager settings, as that seems like it would be excessive, but here are some of the main highlights:
{
"authority": "https://subdomain.appname.com/auth/realms/appname/",
"client_id": "appname-app",
"redirect_uri": "http://localhost:3001/callback",
"response_type": "code",
"extraQueryParams": {"kc_idp_hint": "google"},
"loadUserInfo": true,
"scope": ["openid", "profile", "email"]
}
I'm not sure what else would be helpful to anyone troubleshooting it - I can give my store.js information or my main application index.js if needed.
Scope paramenter for user meneger should be space separated string and not an array.
Example: 'openid profile email'
Try this.
class CallbackPage extends React.Component {
successCallback = (user) => {
this.props.dispatch(push("/settings"))
};
render() {
return (
<CallbackComponent
userManager={userManager}
successCallback={this.successCallback}
errorCallback={error => {
//this.props.dispatch(push("/chat"));
console.error(error);
}}
>
<div>Redirecting TEST...</div>
</CallbackComponent>
);
}
}
export default connect()(CallbackPage);
I am having a hard time to render a nested object on to a reactjs page
import React, { Component } from "react";
import Toolpanel from "./Todopanel";
import Toollist from "./Toollist";
class App extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
city: "Auckland",
cityWeather: {}
};
this.updateUser = this.updateUser.bind(this);
}
updateUser(entry) {
console.log(entry);
let item = {
text: entry,
key: Date.now()
};
this.setState(prevstate => {
return {
users: prevstate.users.concat(item)
};
});
}
componentDidMount() {
let apiId = "***************************";
let city = this.state.city;
let ApiString =
"http://api.openweathermap.org/data/2.5/weather?q=" +
city +
"&APPID=" +
apiId;
fetch(ApiString)
.then(results => results.json())
.then(data => this.setState({ cityWeather: data }));
}
render() {
let test = this.state.cityWeather;
return (
<div>
<Toolpanel parentUpdate={this.updateUser} />
<div>wind speed : {test.wind.speed} </div>
</div>
);
}
}
export default App;
I have added my JSON file that I received from my weather API
//Json file
{
"coord": { "lon": 174.77, "lat": -36.85 },
"weather": [
{
"id": 804,
"main": "Clouds",
"description": "overcast clouds",
"icon": "04n"
}
],
"base": "stations",
"main": {
"temp": 293.7,
"pressure": 1018,
"humidity": 77,
"temp_min": 293.15,
"temp_max": 294.26
},
"visibility": 10000,
"wind": { "speed": 5.1, "deg": 360 },
"clouds": { "all": 92 },
"dt": 1553672420,
"sys": {
"type": 1,
"id": 7345,
"message": 0.0043,
"country": "NZ",
"sunrise": 1553624951,
"sunset": 1553667823
},
"id": 2193733,
"name": "Auckland",
"cod": 200
}
I am trying to render the wind speed from the JSON to my page.. but is throwing me a error message saying "TypeError: Cannot read property 'speed' of undefined"...Please help. I am fairly new to ReactJs.
If you look at the code, here is the sequence of events:
Component is created, i.e. constructor is called
Component is mounted, i.e. componentDidMount is called
componentDidMount starts an async request to fetch the data which is then parsed and set in state.
render method tries to read the data from state.
Now, since the request in #3 is an async one, it may not have completed in time when the render method has been called the first time.
So, you need to check if your request has completed or failed or is running.
You can use that to conditionally render the content in your render method.
Recommended reading
The official reactjs blog entry on async rendering with examples of when data is fetched from an external resource
You're not wrong the way you approached it. The error you're getting is because the fetch you're performing is taking some time, and render first executes without having the data populated.
So first time it gets in your render method the value of test = {}. So test.wind.speed will throw an error.
Instead, show a loading state of some sort or simply return null until the call is performed:
render() {
let test = this.state.cityWeather;
if (!test) {
return 'Loading...';
}
....
}
You are accessing the properties too fast since fetch is an asynchronous call it will take some time but your render fires before that already.
Use it like this
{ test && <div>wind speed : {test.wind.speed} </div>}
Initially your test will be null as you haven't received any response from your API so you should check the variable presence before using it. So just check if it is present before using it like this:
render() {
let test = this.state.cityWeather;
return (
<div>
<Toolpanel parentUpdate={this.updateUser} />
<div>wind speed : {test && test.wind && test.wind.speed ? test.wind.speed : ''} </div>
</div>
);
}
Since you didn't post ToolPanel Component implementation, I may be wrong (I'm missing some information). But, I'm also pretty sure that your problem is not having a loading variable.
Basically, the first time render() method is called, you have this.state.cityWeather to be an empty object {}; that is because you fetch the data in componentDidMount(). Thus, the first time render() is called, being this.state.cityWeather empty, you cannot access this.state.cityWeather.wind.speed because you don't have the property wind in this.state.cityWeather!
So, usually, the common way to do this is adding a property loading in the state, and setting it to true in the constructor. Then, in the callback of the fetch, while you set the data in this.state.cityWeather, you also set loading to true.
Finally, in the render() method you wrote a conditional rendering: if this.state.loading === true, then you print a simple paragraph like <p>I'm retrieving info!</p>, otherwhise, if this.state.loading === false, you can render what you want.