Hey guys,
When it’s about user accounts and cookies, React seems to be a bit harder to master than using pure Javascript.
Here’s the case: my app allows a user to register and log in. When signing in every user will get a cookie set for the individual authorization code and the user’s id. The user id will be used in some API calls through fetch (I don’t use Axios in this project). I just replace a placeholder I used for my project when building it to make it ready for a serious test. So instead of, let’s say “user_id: 1, text_input…” I use “user_id: props.user_id” or so.
The problem that occurs is that everything seems to work occasionally. I’d apply a case from one part of my project, the “skills”-category. There are three major interactions.
- Search and add a skill to a user’s skill list
- List all current skills of a user
- Remove a skill from the user’s skill list
I can say that the functions worked well until I started implementing the ID. I’d usually say there’s a simple problem to fix. But the functions behave inconsistently and strange.
Here’s an example.
I click on remove a skill item and it’s gone. I do it again and it remains. No change happens until I add a skill from the search list. But this mostly changes the list by removing, not adding, sometimes it does.
On another part of the app I have a similar problem, but there it suddenly shows a JSON-error.
The code I use for the skill part is:
skills.js
import React from "react";
import './../App.css';
import auth from './../auth'
import SkillList from "../components/skillsearch/skills.list";
import FindSkills from '../components/skillsearch/skills.find';
class Skills extends React.Component {
constructor(props) {
super(props);
this.state = {
nation: "",
nations: [],
countries: [],
teams: [],
items: [],
selectCountry: [],
selectedCountry: "",
selectedDivision: "",
isLoaded: false,
entityURL: "",
entityName: "",
curWorkHere: false,
error: null,
inputValue: "",
lastDirection: "",
lastSkillID: "",
userID:"",
};
}
componentWillMount(){
this.setState({userID:auth.getCookie('gscover')})
}
handleChange = (e) => {
this.setState({ selectedDivision: e.target.value })
console.log(this.state)
console.log(e.target.value)
}
textChange = inputValue => {
this.setState({ inputValue: inputValue.value });
console.log(inputValue.value);
};
handleLangChange = () => {
var lang = this.dropdown.value;
this.props.onSelectLanguage(lang);
}
/* handleMyChange = (newValue) => {
this.props.setState({ inputValue: newValue });
} */
manageSkill = (direction, skill_id,user_skills_id) => {
let entityweb = this.state.entityURL;
var user_id = this.state.userID;
var direction_string;
console.log(direction+" "+skill_id+" "+user_skills_id)
if(direction==1)
{
direction_string = "add";
}
if(direction==0)
{
direction_string = "delete";
}
console.log(direction+" "+direction_string)
var skillJSONObject = JSON.stringify({"user_id":user_id,"skill_id":skill_id,"user_skills_id":user_skills_id});
fetch("https://myapi.com/v1/skills/user/"+direction_string, {
method: 'POST',
body: skillJSONObject,
headers: {
'Authorization': this.props.code
}})
.then(response => response.json())
.catch(error => {
console.error('Error:', error);
});
console.log(this.state);
this.setState({ inputValue: skill_id, lastDirection:direction,lastSkillID:skill_id });
};
handleMyChange = (temperature) => {
this.setState({inputValue: temperature});
console.log(temperature)
this.manageSkill(1,temperature)
}
refresh = () => {
}
render() {
return <div>
<div className="max-w-2xl mx-auto px-1 pb-8 sm:px-6 lg:px-8 bg-gray-200">
<div>
<FindSkills value={this.state.inputValue} onTemperatureChange={this.handleMyChange} code={this.props.code} user={this.state.userID}/>
</div>
<SkillList manageSkill = {this.manageSkill} refresh={{direction:this.state.lastDirection,skillid:this.state.lastSkillID}} code={this.props.code} user={this.state.userID}/>
</div>
</div>
}
}
export default Skills;
skills.find.js
import React from "react";
import AsyncSelect from 'react-select';
class FindSkills extends React.Component {
constructor(props) {
super(props);
this.handleMyChange = this.handleMyChange.bind(this);
this.state = {
error: null,
isLoaded: false,
selectedCountry: null,
values: [],
inputValue: "",
};
}
componentDidMount() {
fetch('https://myapi.com/v2/admin/listskills')
.then(res => res.json())
.then(res => this.setState({
values: res.data
}))
.catch(error => console.log(error))
}
textChange = inputValue => {
this.setState({ inputValue: inputValue.value });
console.log(inputValue.value);
};
handleMyChange = inputValue => {
// Here, we invoke the callback with the new value
this.props.onTemperatureChange(inputValue.value);
}
render() {
let option = []
if (this.state.values.length > 0) {
this.state.values.forEach(role => {
let roleDate = {}
roleDate.value = role.skill_id
roleDate.label = role.name
option.push(roleDate)
})
}
return (
<div>
<AsyncSelect options={option} value={this.props.value} onChange={this.handleMyChange}/>
</div>
)
}
}
export default FindSkills;
skills.list.js
import React from "react";
import auth from "./../../auth"
class SkillList extends React.Component {
constructor(props) {
super(props);
this.state = {
myinput: "hello",
skill_items: [],
lastDirection: "",
lastSkillID:"",
isLoaded: false,
error: null,
userID:'',
};
}
async componentWillMount(){
this.setState({userID:auth.getCookie('gscover')})
}
getuserSkills = (e) => {
var user_id = this.props.user;
var skillJSONObject = JSON.stringify({ "user_id":user_id, "withvalue": 1 });
fetch("https://myapi.com/v1/skills/all/user", {
method: 'POST',
body: skillJSONObject,
headers: {
'Authorization': this.props.code
}
})
.then(response => response.json())
//.then(json => json.data.map(skill => skill.name))
.then(data => {
//console.log(data.data[0].name)
if (data.hasOwnProperty("response")) {
console.log(data["response"]["data"]);
this.setState({
isLoaded: true,
skill_items: data["response"]["data"]
});
}
else {
console.log(data);
}
}).catch(error => {
console.error('Error:', error);
});
console.log(this.state);
};
componentDidMount() {
this.getuserSkills();
//this.createCV();
}
componentWillReceiveProps(props) {
const { direction, skillid } = this.props;
if (this.state.lastDirection !== direction && this.state.lastSkillID !== skillid) {
this.getuserSkills();
}
}
render() {
const { error, isLoaded, skill_items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div className="pt-5 px-1 md:px-0">
{skill_items.map(item => (
<div>
<div class="flex justify-center bg-gray-100 mb-2">
<div class="w-full max-w-lg pt-4">
<div class="flex">
<a className="my-auto"><p class="text-gray-900 w-20"><a class="text-3xl font-semibold">{item.skill_scoring==null ? "--" : item.skill_scoring*10}</a> <a className={item.scoring_change > 0 ? "text-green-400 font-bold" : item.scoring_change < 0 ? "text-red-400 font-bold" : "text-blue-400 font-bold"}>{item.scoring_change > 0 ? "↑" : item.scoring_change < 0 ? "↓" : ""} {item.scoring_change != null ? "("+item.scoring_change*10+")" : ""}</a></p></a>
<div class="flex-1 pl-4">
<p class="text-gray-500">{item.category}</p>
<div class="flex items-baseline space-x-4">
<h2 class="text-2xl font-semibold">
{item.name}
</h2>
</div>
</div>
<div class="flex-no-shrink">
<p><button class="remBtn p-1 ml-2 border-2 rounded text-red-400 border-red-400 hover:text-white hover:bg-red-400">x<a className="remText" onClick= { () => this.props.manageSkill(0,item.skill_id,item.user_skills_id)}> Remove</a></button></p>
</div>
</div>
<div class="flex p-1 border-t border-gray-300 text-gray-700">
<div class="flex-1 mx-auto inline-flex items-center">
<p className="mx-auto"><span class="text-gray-900 font-bold">{item.ratings!=null ? item.ratings : "0"}</span> Grades<span className="text-gray-400 text-sm"> last 30d</span></p>
</div>
<div class="flex-1 mx-auto inline-flex items-center text-center hover:text-blue-400 hover:bg-blue-100">
<p className="mx-auto"><a href={"rateme/user/"+item.category}>Share</a></p>
</div>
</div>
</div>
</div>
</div>
))}
</div>
)
}
}}
export default SkillList;
Because the function is requested from time to time here’s the getCookie-function from auth.js
getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
Maybe I make a simple mistake or it’s not best practice. But the strange behavior of the functions mentioned above is confusing me, so I guess there’s a speed or storing issue? Maybe you can give me a hint to the solution.