import React, {useState, useEffect, useContext} from 'react'
import AuthContext from '../context/AuthContext'
const HomePage = () => {
const [note,setNote] = useState([])
let {authTokens} = useContext(AuthContext)
useEffect(()=>{
getNotes()
},[])
let getNotes = async () => {
let response = await fetch("http://127.0.0.1:8000/api/notes",{
method:"GET",
headers:{
"Content-type":"application/json",
"Authorization":"Bearer "+String(authTokens.access)
}
})
let data =await response.json();
console.log("data i have been wait",data)
console.log("Data in it",note)
}
return (
<div>
<p>You are logged in to home paage</p>
{/* {notes.map(note =>{
<li >{note.body}</li>
})} */}
</div>
)
}
export default HomePage
I got my data from django backend and try to show in react using map but it doesnt map.
I can get the data but when I am trying to setNote(data) it doesn't not work.
Sometimes it works but still cant map it. I cant refresh map func and put data on it
and I don't get any error.[console.log like that][1]
console.log("data i have been wait",data)
setNote(data)
console.log("Data in it",note)```
[1]: https://i.stack.imgur.com/h17QF.png
In the case the setState works correctly but you can't map the data, I suggest that the problem comes from the fact that you put brackets inside your map but no return.
Try this instead:
{notes.map(note =>
{
return(<li >{note.body}</li>);
}
)}
or with arrow function:
{notes.map(note => <li>{note.body}</li>)}
Related
In a regular React App I'd use Redux to manage the state, where I'd dispatch the initial data before matching any route in App, however, Redux is not advised in Remix, so I'm using useContext instead.
Is there a way to call loaders to fetch initial data (e.g. session, objects, etc.) before/without having to match any route and to then store that data in the context global store and then can be accessed by any component whithin the store? That way, the API will only be called during app initialization.
I'm at this moment calling the initial data in the loader of root.tsx, getting it with useLoaderData and then passing it as a prop to StoreProvider to dispatch it in the global state, however, I don't think this should be done like that way.
export let loader: LoaderFunction = async ({ request }) => {
let user = await getUser(request);
const products = await db.product.findMany();
return { user: user?.username, products };
};
function App() {
const data = useLoaderData<LoaderData>();
return (
<html lang="en">
...
<StoreProvider initData={data}>
<body>
...
<Outlet />
<ScrollRestoration />
<Scripts />
{process.env.NODE_ENV === "development" && <LiveReload />}
</body>
</StoreProvider>
</html>
);
}
export default App;
I think doing the data loading on the root route loader is the best way.
If you don't like that approach you could also fetch on entry.server and entry.client.
For example in entry.client you probably have something like this:
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";
hydrate(<RemixBrowser />, document);
So you can change it to do the fetch before calling hydrate.
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";
fetch(YOUR_API_ENDPOINT)
.then(response => response.json())
.then(data => {
hydrate(
<YourContextProvider value={data}>
<RemixBrowser />
</YourContextProvider>,
document
)
});
And in entry.server you can change the handleRequest function to something like this:
import { renderToString } from "react-dom/server";
import { RemixServer } from "remix";
import type { EntryContext } from "remix";
export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
let response = await fetch(YOUR_API_ENDPOINT)
let data = await response.json()
let markup = renderToString(
<YourContextProvider value={data}>
<RemixServer context={remixContext} url={request.url} />
</YourContextProvider>
);
responseHeaders.set("Content-Type", "text/html");
return new Response("<!DOCTYPE html>" + markup, {
status: responseStatusCode,
headers: responseHeaders
});
}
By doing it on entry.client and entry.server the fetch will only happen once and it will never be triggered again.
I still recommend you to do it inside the loader of the root so after an action it can be fetched again to keep the data updated.
I'm working on a web app with NextJS, Apollo and React (hooks).
I have a form that asks the name of the visitor as the first step in a registration process.
When submitting the form the name will be saved in the Apollo cache and the visitor gets redirected to the next page.
import React, { useState } from 'react';
import Router , {useRouter} from 'next/router';
import { useApolloClient } from '#apollo/react-hooks';
const NameForm = props => {
const [name, setName] = useState("");
const client = useApolloClient();
const router = useRouter();
const handleSubmit = e => {
e.preventDefault();
if(!name) return;
client.writeData({ data: { name } });
router.push('/user/register');
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name">Naam</label>
<div>
<input type="text" id="name" name="name" value={name} onChange={e => setName(e.target.value)} />
<button type="submit" onClick={handleSubmit}>Get started</button>
</div>
</div>
</form>
)
}
export default NameForm;
The next page contains a more extensive form. When visitors come from the homepage, the name is already known and I want to get it back from the cache. I thought
import { gql } from 'apollo-boost';
import { useApolloClient } from '#apollo/react-hooks';
import AddUserForm from '../../components/forms/AddUserForm';
const GET_NAME = gql`
query GetName {
name #client
}`;
const AddUser = ({ name }) => (
<React.Fragment>
<AddUserForm name={name} />
</React.Fragment>
)
AddUser.getInitialProps = async ctx => {
const client = useApolloClient();
const name = await client.cache.readQuery({ query: GET_NAME });
return { name: name || '' };
}
export default AddUser;
I thought I could do this in the getInititialProps hooks are only allowed in the body of a functional component.
Because of the continuous development of next, react hooks and apollo I'm missing a tutorial/course about this and I find it difficult to find a right way to do this.
I hope someone here can help me further.
use apollo-client cache can lead you to some questions that really depends on the apollo-client's implementation and nextjs implementation.
If you open your app by entering the url to the browser address bar, Next.js will make requests (assuming the view need to fetch data) from server-side, then send to the client the rendered HTML.
Because apollo-client fetch then cache the data from server side, then the question is "Does Next.js send the apollo-client with its cache to client side for next request?"
You cannot sure about this unless you understand clearly about Next.js and apollo-client cache (about its implementation or how it works inside, if apollo cache data in-memory on server-side, you will fail if you go this way)
The answer is unsure because it depends on two stuffs at the same time. And maybe changed on the future!
So to deal with this problem, just use the Next.js way, it has designed a tunnel for data, it is the query on the url.
const NameForm = props => {
const [name, setName] = useState("");
const client = useApolloClient();
const router = useRouter();
const handleSubmit = e => {
e.preventDefault();
if(!name) return;
router.push(`/user/register?name=${name}`);
}
//render ...
}
import { useRouter } from 'next/router';
import AddUserForm from '../../components/forms/AddUserForm';
const AddUser = () => {
const router = useRouter();
return (
<React.Fragment>
<AddUserForm name={router.query.name} />
</React.Fragment>
)
}
export default AddUser;
If you want to send an object instead of a string?
const data = { name: "FoxeyeRinx", email: "foxeye.rinx#gmail.com" };
const base64 = btoa(JSON.stringify(data));
router.push(`/user/register?data=${base64}`);
const AddUser = () => {
const router = useRouter();
const base64 = router.query.data;
//decode base64 then parse it to js object
const data = JSON.parse(atob(base64));
return (
<React.Fragment>
<AddUserForm data={data}/>
</React.Fragment>
)
}
If you think the query is ugly and want to hide the query, use this guide: https://nextjs.org/learn/basics/clean-urls-with-dynamic-routing
error:
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons
Hello I am trying to use useDispatch in my action but it is generating this error from invalid hoook
I can't solve it
can anybody help me?
my action
import {FETCH_FAIL,FETCH_LOADING,FETCH_SUCESS} from './actionType';
import api from '../../../services/api';
import { useDispatch } from "react-redux";
const FetchSucess = data => (console.log(data),{
type:FETCH_SUCESS,
data
});
const FetchFailed = error => ({
type:FETCH_FAIL,
error
});
const isLoadingFetch = () => ({type: FETCH_LOADING})
export default function AllProducts () {
const dispatch = useDispatch()
dispatch(isLoadingFetch());
// fetching data
api.get('/products')
.then( response => { dispatch(FetchSucess(response.data))})
.catch( err => { dispatch(FetchFailed(err.message));});
}
my component
import React, { useState, useEffect } from 'react';
export default function Cards() {
useEffect(() => {
// This will be invoked only once.
getAllProducts();
}, []);
const classes = useStyles();
const classes2 = useStyles2();
const products = useSelector(state => state.data.filteredProducts);
return (
<div className="App">
<Container maxWidth="md" className={classes.root}>
<Grid container md={4} spacing={1} ></Grid>
<Grid container md={8} spacing={1} alignItems={"center"}>
{products.map(product => (
<Grid item lg={4} md={4} sm={12} xs={12}>
<Card className={classes2.card}>
<CardMedia
className={classes2.media}
image={
"https://www.theclutch.com.br/wp-content/uploads/2019/11/skins-csgo-neymar.jpg"
}
/>
<CardContent className={classes2.content}>
<Typography
className={classes2.name}
variant={"h6"}
gutterBottom
>
{product.name}
</Typography>
<Typography
className={classes2.price}
variant={"h1"}
>
{util.formatCurrency(product.price)}
</Typography>
</CardContent>
</Card>
</Grid>
))}
</Grid>
</Container>
</div>
);
}
Based on this comment above:
All product is my action
If AllProducts is a Redux action that needs to perform an async operation and dispatch other actions in response to that operation, there's a convention available by which Redux will pass dispatch as a function argument. The action just needs to return a function which accepts that argument. For example:
export default function AllProducts () {
return function(dispatch) {
dispatch(isLoadingFetch());
// fetching data
api.get('/products')
.then( response => { dispatch(FetchSucess(response.data))})
.catch( err => { dispatch(FetchFailed(err.message));});
}
}
There's no need to use the hook, that's only necessary within React Function Components or within other hooks (which themselves are used within React Function Components).
I am busy with a little proof of concept where basically the requirement is to have the home page be a login screen when a user has not logged in yet, after which a component with the relevant content is shown instead when the state changes upon successful authentication.
I have to state upfront that I am very new to react and redux and am busy working through a tutorial to get my skills up. However, this tutorial is a bit basic in the sense that it doesn't deal with connecting with a server to get stuff done on it.
My first problem was to get props to be available in the context of the last then of a fetch as I was getting an error that this.props.dispatch was undefined. I used the old javascript trick around that and if I put a console.log in the final then, I can see it is no longer undefined and actually a function as expected.
The problem for me now is that nothing happens when dispatch is called. However, if I manually refresh the page it will display the AuthenticatedPartialPage component as expected because the localstorage got populated.
My understanding is that on dispatch being called, the conditional statement will be reavaluated and AuthenticatedPartialPage should display.
It feels like something is missing, that the dispatch isn't communicating the change back to the parent component and thus nothing happens. Is this correct, and if so, how would I go about wiring up that piece of code?
The HomePage HOC:
import React from 'react';
import { createStore, combineReducers } from 'redux';
import { connect } from 'react-redux';
import AuthenticatedPartialPage from './partials/home-page/authenticated';
import AnonymousPartialPage from './partials/home-page/anonymous';
import { loggedIntoApi, logOutOfApi } from '../actions/authentication';
import authReducer from '../reducers/authentication'
// unconnected stateless react component
const HomePage = (props) => (
<div>
{ !props.auth
? <AnonymousPartialPage />
: <AuthenticatedPartialPage /> }
</div>
);
const mapStateToProps = (state) => {
const store = createStore(
combineReducers({
auth: authReducer
})
);
// When the user logs in, in the Anonymous component, the local storage is set with the response
// of the API when the log in attempt was successful.
const storageAuth = JSON.parse(localStorage.getItem('auth'));
if(storageAuth !== null) {
// Clear auth state in case local storage has been cleaned and thus the user should not be logged in.
store.dispatch(logOutOfApi());
// Make sure the auth info in local storage is contained in the state.auth object.
store.dispatch(loggedIntoApi(...storageAuth))
}
return {
auth: state.auth && state.auth.jwt && storageAuth === null
? state.auth
: storageAuth
};
}
export default connect(mapStateToProps)(HomePage);
with the Anonymous LOC being:
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { loggedIntoApi } from '../../../actions/authentication';
export class AnonymousPartialPage extends React.Component {
constructor(props) {
super(props);
}
onSubmit = (e) => {
e.preventDefault();
const loginData = { ... };
// This is where I thought the problem initially occurred as I
// would get an error that `this.props` was undefined in the final
// then` of the `fetch`. After doing this, however, the error went
// away and I can see that `props.dispatch is no longer undefined
// when using it. Now though, nothing happens.
const props = this.props;
fetch('https://.../api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(loginData)
})
.then(function(response) {
return response.json();
})
.then(function(data) {
if(data && data.jwt) {
props.dispatch(loggedIntoApi(data));
localStorage.setItem('auth', JSON.stringify(data));
}
// else show an error on screen
});
};
render() {
return (
<div>
... onSubmit gets called successfully somewhere in here ...
</div>
);
}
}
export default connect()(AnonymousPartialPage);
the action:
// LOGGED_INTO_API
export const loggedIntoApi = (auth_token) => ({
type: 'LOGGED_INTO_API',
auth: auth_token
});
// LOGGED_OUT_OF_API
export const logOutOfApi = (j) => ({
type: 'LOG_OUT_OF_API'
});
and finally the reducer:
const authDefaultState = { };
export default (state = authDefaultState, action) => {
switch (action.type) {
case 'LOGGED_INTO_API':
// SOLUTION : changed this line "return action.auth;" to this:
return { ...action.auth, time_stamp: new Date().getTime() }
case 'LOG_OUT_OF_API':
return { auth: authDefaultState };
default:
return state;
}
};
My suggestion would be to make sure that the state that you are changing inside Redux is changing according to javascript's equality operator!. There is a really good answer to another question posted that captures this idea here. Basically, you can't mutate an old object and send it back to Redux and hope it will re-render because the equality check with old object will return TRUE and thus Redux thinks that nothing changed! I had to solve this issue by creating an entirely new object with the updated values and sending it through dispatch().
Essentially:
x = {
foo:bar
}
x.foo = "baz"
dispatch(thereWasAChange(x)) // doesn't update because the x_old === x returns TRUE!
Instead I created a new object:
x = {
foo:"bar"
}
y = JSON.parse(JSON.stringify(x)) // creates an entirely new object
dispatch(thereWasAChange(y)) // now it should update x correctly and trigger a rerender
// BE CAREFUL OF THE FOLLOWING!
y = x
dispatch(thereWasAChange(y)) // This WON'T work!!, both y and x reference the SAME OBJECT! and therefore will not trigger a rerender
Hope this helps!
Im trying to get my json result into my react code
The code looks like the following
_getComments() {
const commentList = "AJAX JSON GOES HERE"
return commentList.map((comment) => {
return (
<Comment
author={comment.author}
body={comment.body}
avatarUrl={comment.avatarUrl}
key={comment.id} />);
});
}
How do i fetch AJAX into this?
First, to fetch the data using AJAX, you have a few options:
The Fetch API, which will work out of the box in some browsers (you can use a polyfill to get it working in other browsers as well). See this answer for an example implementation.
A library for data fetching (which generally work in all modern browsers). Facebook recommends the following:
superagent
reqwest
react-ajax
axios
request
Next, you need to use it somewhere in your React component. Where and how you do this will depend on your specific application and component, but generally I think there's two scenarios to consider:
Fetching initial data (e.g. a list of users).
Fetching data in response to some user interaction (e.g. clicking a
button to add more users).
Fetching initial data should be done in the life-cycle method componentDidMount(). From the React Docs:
var UserGist = React.createClass({
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
};
},
componentDidMount: function() {
this.serverRequest = $.get(this.props.source, function (result) {
var lastGist = result[0];
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
return (
<div>
{this.state.username}'s last gist is
<a href={this.state.lastGistUrl}>here</a>.
</div>
);
}
});
ReactDOM.render(
<UserGist source="https://api.github.com/users/octocat/gists" />,
mountNode
);
Here they use jQuery to fetch the data. While that works just fine, it's probably not a good idea to use such a big library (in terms of size) to perform such a small task.
Fetching data in response to e.g. an action can be done like this:
var UserGist = React.createClass({
getInitialState: function() {
return {
users: []
};
},
componentWillUnmount: function() {
this.serverRequest && this.serverRequest.abort();
},
fetchNewUser: function () {
this.serverRequest = $.get(this.props.source, function (result) {
var lastGist = result[0];
var users = this.state.users
users.push(lastGist.owner.login)
this.setState({ users });
}.bind(this));
},
render: function() {
return (
<div>
{this.state.users.map(user => <div>{user}</div>)}
<button onClick={this.fetchNewUser}>Get new user</button>
</div>
);
}
});
ReactDOM.render(
<UserGist source="https://api.github.com/users/octocat/gists" />,
mountNode
);
Lets take a look on the fetch API : https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
Lets say we want to fetch a simple list into our component.
export default MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
lst: []
};
this.fetchData = this.fetchData.bind(this);
}
fetchData() {
fetch('url')
.then((res) => {
return res.json();
})
.then((res) => {
this.setState({ lst: res });
});
}
}
We are fetching the data from the server, and we get the result from the service, we convert is to json, and then we set the result which will be the array in the state.
You can use jQuery.get or jQuery.ajax in componentDidMount:
import React from 'react';
export default React.createClass({
...
componentDidMount() {
$.get('your/url/here').done((loadedData) => {
this.setState({data: loadedData});
});
...
}
First I'd like to use fetchAPI now install of ajax like zepto's ajax,the render of reactjs is asyn,you can init a state in the constructor,then change the state by the data from the result of fetch.