localStorage.getItem ('token') returns null - javascript

In the Items component I set the toke in localstorage. The Details component tries to access this token. It gets null
class Items extends Component {
constructor (props) {
super(props);
}
getT = () => {
axios({
method: 'post',
url: '/oauth2/token',
data,
config
})
.then(res => {
if (res.status === 200) {
console.log(res.data)
this.setState({
token: res.data
})
localStorage.setItem('token', JSON.stringify(res.data['access_token']))
} else {
const error = new Error(res.error);
throw error;
}
}).catch(err => {
console.error(err);
alert('Error logging in please try again');
});
}
render () {
return (
<div>
<ul className="instancesUl">
{ this.props.items.map((item, index) =>
<li
key={item.id}
item={item}
onClick = {this.getT}
>
</li>
)
}
</ul>
<Details
/>
</div>
)
}
}
class Details extends Component {
constructor (props) {
super(props);
this.state = {
}
axios.defaults.headers.common['Authorization'] = localStorage.getItem('token');
}
componentDidMount() {
axios({
url: `https://app`,
method: "GET"
})
.then(res => {
})
.catch(error => {
})
}
render () {
return (
<div >
</div>
)
}
}

The problem is that Details constructor is invoked earlier at the time when the localStorage is empty, after that your logic to set the token works but Details constructor does not execute again to get the updated value.
Here is an example how you could tweak your code
In short you should not rely on the state everywhere, init your state in a parent component and setState there, pass it properties to the children components via React props, so when you change the state your props will get updated also and force the component which they belong to render the update and call all needed logic.

You are reading before the write.
Root of Problem:
getT method of class Items is responsible for writing the
localstorage and which has async call, It will take some time to get
data from server so you don't know when it will return.
Details component is rendered inside the Items component so it will
be called immediately when it reaches the render method and as discussed in
above point we are not sure token is written is localstorage or not.
Solution:
You can set some value in state saying token_is_saved
You can render Details component only when token_is_saved is true
Initialize state in Items constructor,
this.state = {
token: null
}
Modify your Items's render method as,
{
this.state.token && <Details />
}

Constructor of Details component works once in the very beginning while instantiating the component class and in that case there would be not token available in the localStorage. After if you click on the button, which later makes the call to the server and after promise is being resolved, you store the token. After refreshing your page, it will become available for you.

Is this the correct way of implementation?
import Vue from 'vue';
import axios from 'axios';
const baseURL = "http://127.0.0.1:8000/api/v1";
const token = localStorage.getItem('token');
export default axios.create({
baseURL,
headers: {
'Content-type' : 'application/json',
'Authorization' : 'Bearer ${token}'
}
});

Related

how to fetch url that contains an array dynamically

im having trouble figuring out how to fetch a url that contains an array in react
the parent component fetches data that gets sent to two components.
export default class ParentComponent extends Component<AuthProps, ChannelState> {
constructor(props: AuthProps) {
super(props)
this.state = {
...
}
}
getChannel = () => {
console.log("get channel called")
fetch(`${APIURL}/channel/mine`, {
method: "GET",
headers: new Headers({
"Content-Type": "application/json",
"Authorization": `${this.props.sessionToken}`
})
})
.then(response => response.json())
.then(data => {
console.log(data)
this.setState({
channel: data
})
console.log(this.state.channel, "channel called")
})
.catch(err => console.log(err))
}
the state gets sent to two child components. childcomponent1 is a route that uses channelId in the fetch method. childcomponent2 displays a dynamic link to component1 using channelId as a key
export default class ChildComponent1 extends Component<AuthProps, ChannelEntryState> {
constructor(props: AuthProps) {
super(props)
this.state = {
...
}
}
getChannelEntry = () => {
console.log("get channel entry called")
console.log(this.props.channel.length)
fetch(`${APIURL}/channel/${this.props.channel[1].channelId}/channelentry`, {
method: "GET",
headers: new Headers({
"Content-Type": "application/json",
"Authorization": `${this.props.sessionToken}`
})
})
.then(response => response.json())
.then(data => {
console.log(data)
this.setState({
channelEntry: data.messages
})
console.log(this.state.channelEntry, "channel entry called")
})
.catch(err => console.log(err))
}
const ChildComponent2 = (props: AuthProps) => {
return(
<Row>
{props.channel.map((cprops: ChannelType) => {
return(
<>
<Col>
<div>
<ul className="sidebar-list list-unstyled" key={cprops.channelId}>
<li><Link to={`/channelEntry/${cprops.channelId}`}><Button onClick={() => {console.log('button clicked')}}>{cprops.name}</Button></Link></li>
</ul>
</div>
</Col>
</>
)
})}
Ive looked into useParams but i believe its only possible in a functional component. I believe i shouldnt use functional components when states can change. How can i fetch the url in component1 dynamically.
Concerning params you can access them in a react class component using this.props.match.params.
And concerning the useParams, two things
yes, anything with a use in front of a name should be a hook and can only be used in functional components.
no, functional components, since React v17, can have their own states, using the useState hook.
just keep in mind that the you can have multiple states in functional components so you should use a state for each controlled part.
for people using react typescript class components look into this link
React-router-v6 access a url parameter

Issue displaying data when passing props in React

I'm trying to understand why this is throwing an error of name is undefined, but if I do it a different way, I can get data to display... Been stuck on this for quite awhile now and can't pass data to other components. This is the closest I've come, but don't know why one way works but the other says undefined. Trying to select and pass data from JobsTableApi.js to Title.js
JobsTableApi.js:
import React, { Component } from 'react'
//import Title from './components/header/Title.js'
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXX_XXXX_XXXXXXXXXXXXXX',
'Content-Type': 'application/json'
};
class JobsTableApi extends Component {
constructor(props) {
super(props);
this.state = {
data: null,
};
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
let body = {"from":"bpz99ram7","select":[3,6,80,81,82,83,86,84,88,89,90,91,92,93,94,95,96,97,98,99,101,103,104,105,106,107,109,111,113,115,120,123,224,225,226,227,228,229,230,231,477,479,480,481],"where": "{40.CT. 'In Progress'}","sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":40,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query', {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
}).then(response => response.json())
.then( data => this.setState({ data })
);
}
render() {
const { data } = this.state;
if (data === null) return 'Loading Job Data... ';
return (
<div>
{Object.keys(data["data"]).map(item => (
<div key = {item}>
{data["data"][item][6].value}
</div>
))}
</div>
)
}
}
export default JobsTableApi;
Title.js:
import { React } from 'react';
import PropTypes from 'prop-types';
import JobsTableApi from '../../JobsTableApi';
export default function Title() {
return(
<div>
<h3>
<JobsTableApi />
</h3>
</div>
)
}
Title.propTypes = {
name: PropTypes.string.isRequired
}
The above methods display all data coming over from my api call that has the field ID of 6 accurately, but since i'm attempting to pull different fields over to different components, I need to set it as props, but when I do, I get errors and says undefined. Example below.
JobsTableApi.js:
import React, { Component } from 'react'
import Title from './components/header/Title.js'
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXXXX_XXXX_XXXXXXXXXXXXXX',
'Content-Type': 'application/json'
};
class JobsTableApi extends Component {
constructor(props) {
super(props);
this.state = {
data: null,
};
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
let body = {"from":"bpz99ram7","select":[3,6,80,81,82,83,86,84,88,89,90,91,92,93,94,95,96,97,98,99,101,103,104,105,106,107,109,111,113,115,120,123,224,225,226,227,228,229,230,231,477,479,480,481],"where": "{40.CT. 'In Progress'}","sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":40,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query', {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
}).then(response => response.json())
.then( data => this.setState({ data })
);
}
render() {
const { data } = this.state;
if (data === null) return 'Loading Job Data... ';
return (
<div>
{Object.keys(data["data"]).map(item => (
<div key = {item}>
<Title name = {data["data"][item][6].value} />
</div>
))}
</div>
)
}
}
export default JobsTableApi;
Title.js:
import { React } from 'react';
import PropTypes from 'prop-types';
//import JobsTableApi from '../../JobsTableApi';
export default function Title({ name }) {
return(
<div>
<h3>
{name}
</h3>
</div>
)
}
Title.propTypes = {
name: PropTypes.string.isRequired
}
As you can see from my api call in the 'body' I'm pulling over many fields with values. From here I need to send these throughout my app into different components to be used.
Right now I have 2 api call files in my src folder, then src>components>charts>MultipleLineCharts.js files. As well as src>components>header>Title.js
I'm wondering if I need to change my structure and make the Api Calls on a parent component? Instead of this as siblings? App.js in src is rendering all. Any advice or guidance on this would be appreciated as well.
Thanks!
If the components you need to send the data to are all direct children of this parent API fetching component, then in the interests of starting simple, you don't need to use anything like React Context or Redux - keep it simple and get it working, then iterate as required.
The moment you build out a complex component hierarchy and unrelated or deeply-nested components need access to the data, then you probably want to look at something like Context / Redux.
`Right now I have 2 api call files in my src folder, then src>components>charts>MultipleLineCharts.js files. As well as src>components>header>Title.js
I'm wondering if I need to change my structure and make the Api Calls on a parent component? Instead of this as siblings?
`
You can use React Context or Redux for state management, So you can send or use data to any component.

how to fetch json data and render component (List) according to the response in ReactJS

I want to load book list (book files) from /list endpoint and list them in <ul>. I created a list component and then import it on index.jsx. But it doesn't work. How can I render component with json data fetched from server in body?
list.component.jsx:
import React from 'react'
class List extends React.Component {
constructor() {
super()
this.state = { files: [] }
}
componentDidMount() {
let files = []
fetch('/books', {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ dir: '/' })
})
.then(response => response.json())
.then(data => {
this.setState({ files: data.books })
})
}
render() {
return (
<div>
<h1>Book List</h1>
<ul>
{this.state.files.map(book => {
return <li key={`book-${book.id}`}>{book.name}</li>
})}
</ul>
</div>
)
}
}
export default List
index.jsx:
import List from '../components/list'
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<List/>,
document.body.appendChild(document.createElement('div')),
)
})
I see that '/list' fetched in Network tab but no data on the browser.
Errors given:
list.jsx:31 Uncaught TypeError: this.state.files.map is not a function
at List.render (list.jsx:31)
list.jsx:31 Uncaught (in promise) TypeError: this.state.files.map is not a function
at List.render (list.jsx:31)
Have you tried console.log statements directly before the setState command? Might be worth inspecting data and then data.books. Your data might actually be in data and not data.books. data.books might be a string and you may need to JSON.parse it to convert to an array.
Please follow the link https://codesandbox.io/s/2p5p0n4lpn
Just check using ternary operator this.state.files is data available or not. If data is available then render.
{this.state.files
? this.state.files.map(book => {
return {book.userId};
})
: null}

React - How to pass returned data from an exported function to a component?

How can I pass data I receive from a get request pass over to a component? Whatever I tried wouldn't work but my thinking was as the code below shows..
Thanks!
export function data() {
axios.get('www.example.de')
.then(function(res) {
return res.data
})
.then(function(data) {
this.setState({
list: data
})
})
}
import {data} from './api.js';
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
list: ""
};
}
componentWillMount() {
data();
}
render() {
return <p > this.state.list < /p>
}
}
You call this.setState inside of data()->then callback, so this is context of the then callback function. Instead you should use arrow functions (it does not have its own context) and pass component's this to data function using call
export function data() {
axios.get('www.example.de')
.then(res => res.data)
.then(data => {
this.setState({
list: data
})
})
}
import {data} from './api.js';
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
list: ""
};
}
componentWillMount() {
data.call(this);
}
render() {
return <p > this.state.list < /p>
}
}
However, your data services must not know about setState and, event more, expect passing this from react component. Your data service must be responsible for retrieving data from server, but not for changing component state, see Single responsibility principle. Also, your data service can be called from another data service. So your data service should return promise instead, that can be used by component for calling setState.
export function data() {
return axios.get('www.example.de')
.then(res => res.data)
}
and then
componentWillMount() {
data().then(data=>{
this.setState({
list: data
})
});
}
your api shouldn't know anything about your component, you can easily do this with callback, like so -
export function data(callback) {
axios.get('www.example.de')
.then(res => callback({ data: res.data }))
.catch(err => callback({ error: err }));
}
By doing this you can easily unit test your api
So in your Test component, you simply do -
componentWillMount() {
data(result => {
const { data, error } = result;
if (error) {
// Handle error
return;
}
if (data) {
this.setState({ list: data });
}
});
}
Your request is a promise so you can simply return that from the imported function and use the eventual returned result of that within the component. You only want to be changing the state of the component from within the component.
export function getData(endpoint) {
return axios.get(endpoint);
}
Note I've changed the name of the function to something more "actiony".
import { getData } from './api.js';
class Test extends React.Component {
constructor(props) {
super(props);
// Your state is going to be an array of things, so
// initialise it with an array to spare confusion
this.state = { list: [] };
}
// I use ComponentDidMount for fetch requests
// https://daveceddia.com/where-fetch-data-componentwillmount-vs-componentdidmount/
componentDidMount() {
// We've returned a promise from `getData` so we can still use the
// promise API to extract the JSON, and store the parsed object as the
// component state
getData('www.example.de')
.then(res => res.data)
.then(list => this.setState({ list }))
}
}
Your external function doesn't have the correct context of this, so you'll need to call it with the correct context from within the component:
componentWillMount() {
data.call(this);
}
However, inside the API call, it still won't have the correct this context, so you can set a variable to point to this inside the data() function:
export function data() {
let that = this;
axios('http://www.url.com')
.then(function(res) {
return res.data
})
.then(function(data) {
that.setState({
list: data
})
})
}
Details of the this keyword
However, it's generally considered better practice to only handle your state manipulation from with the component itself, but this will involve handling the asynchronous nature of the GET request, perhaps by passing in a callback to the data() function.
EDIT: Updated with asynchronous code
//api.js
data(callback){
axios.get('www.url.com')
.then(res => callback(res));
}
//component.jsx
componentWillMount(){
data(res => this.setState({list: res}));
}

Undefined State when pulling data for mount

I'm pulling data from my my database which needs to be available prior to the mounting of the component in order for the page to be populated with the componentDidMount() lifecycle method. I've verified that if i remove the setState and console.log my data, it does fetch from the DB as expected, but when I try to assign the data to my state variable, it return a error stating Unable to get property 'setState' of undefined or null reference within my componentWillMount() lifecycle method. I've listed my ReactJS code below.
import React, { Component, PropTypes } from 'react';
import Picture from '../../components/picture.jsx';
import { browserHistory } from 'react-router';
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {clothingData: ''};
}
componentWillMount(){
fetch('/t')
.then(function(result){
return result.json();
})
.then(function(re){
this.setState({ clothingData: re });
console.log(this.state.clothingData);
})
.catch(function(error){
console.log(error);
});
}
componentDidMount(){
//empty for now
}
render(){
var MyArray = ['justin','tiffany','joe','john','karissa','pam','joseph','sean','kim'];
var imageSrc = ['http://placehold.it/249x373','http://placehold.it/249x373','http://placehold.it/249x373','http://placehold.it/249x373','http://placehold.it/249x373',
'http://placehold.it/249x373', 'http://placehold.it/249x373', 'http://placehold.it/249x373'];
return (
<div>
<Picture src = {imageSrc} onClick = { () => {browserHistory.push('/Product'); }} name = {MyArray} amount = {8} />
</div>
);
}
}
The problem is that this is being reassigned from the component instance to the function instance/global object.
componentWillMount() {
fetch('/t')
.then((result) => {
return result.json();
})
.then((re) => {
this.setState({ clothingData: re });
console.log(this.state.clothingData);
})
.catch(function(error){
console.log(error);
});
}
will work just fine since the arrow function will ensure that the this is bound to the component instance so this.setState will actually be defined. Whereas what you have the this is being set to the global object which does not have a property of setState

Categories