I have a problem where in I'm going to access a state inside a method inside my parent component from my child component it returns me an undefined value which i'm sure in the first place have a value of objects in an array.
Parent Component:
class BoardList extends React.Component {
constructor(props){
super(props);
this.state = {
lists: []
};
}
componentWillMount(){
this.props.getBoardLists()
.then((result) => {
this.setState({
lists: result
});
})
.catch(error => {
console.log(error);
});
}
addBoardLists(result){
// This is i'm getting my undefine state lists :(
console.log(this.state.lists);
this.setState({
lists: this.state.lists.concat([result])
});
}
render() {
const { isLoading,data } = this.props;
if(isLoading){
return (
<Loading />
);
}
return (
<div className={style.boardListContainer}>
<h1 className={style.boardListTitle}>Personal Board</h1>
<Row>
<BoardItem item={this.state.lists} />
<BoardAdd onDisplay={this.fetchBoardLists} onAddItem={this.addBoardLists} />
</Row>
</div>
)
}
}
Child Component:
class BoardAdd extends React.Component {
constructor(props){
super(props);
this.state = {
name: '',
boardAddModalShow: false
}
}
openAddBoardModal(){
this.setState({ boardAddModalShow: true });
}
closeAddBoardModal(){
this.setState({ boardAddModalShow: false });
this.props.dispatch(reset('BoardAddModalForm'));
}
addBoard(formProps) {
this.props.addBoard(formProps).then((result) => {
// This is where I access my addOnItem from my parent component
this.props.onAddItem(result);
this.props.dispatch(reset('BoardAddModalForm'));
this.closeAddBoardModal();
})
.catch(error => {
console.log("error");
console.log(error);
});
}
}
Perhaps this will help?
class BoardList extends React.Component {
constructor(props){
super(props);
this.state = {
lists: []
};
this.addBoardList.bind(this)
}
What is this magical .bind? You should read up on what this means in JavaScript (which it almost never thinks what you think it means). By default, ES6 constructors do not bind (for some crazy reason in my opinion), their own methods to their own this value. Thus, the this in your method is referring to a completely different this you are thinking of and consequentially, making this scenario quite bizarre.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/this
Related
I have a hard time finding the answer to this question. Say I have the following script (which doesn't work):
class ProfileCard extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: {},
};
}
componentDidMount() {
fetch("/accounts/api/" + username)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
}
This is what result and this.state.items look like, respectively:
{last_login: "2020-06-25T09:50:24.218Z", is_superuser: "true", is_staff: "true", …}
{}
How can I make the this.state.items have the exact same content as result? Please assume that all variables and methods used have been declared.
how are you?
How is your render()? Can you specify more your question?
For me, the way to put the results inside the items is right, but it only sets the state and changes the content of items after rendering.
Below an example:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
image: ''
}
this.urlImage = this.urlImage.bind(this);
}
urlImage() {
fetch("https://dog.ceo/api/breeds/image/random")
.then( (res) => res.json())
.then((results) => this.setState({ image: results }))
}
componentDidMount() {
this.urlImage()
}
render() {
const { image } = this.state;
if (image === '') return <h2>Loading...</h2>
return (
<div>
<h2>Dogs!</h2>
<div>
<img src={image.message} alt={'Dog'}/>
</div>
</div>
);
}
}
Sorry if you understand that I do not interpret your question correctly, but want to complement that render() was used as an example to show that this.setState() is asynchronous, so with render() you can use this change of state, or you could use componentDidUpdate() that is also executed in the state receives a new value.
componentDidUpdate () {
console.log (this.state.items)
}
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
In fact, you need to specify a question better, because a simple "my status is like this and I wish it could be like" this not described, I hope you can do what you want. Thanks!
I'm trying to build a little weather widget, where the geolocation of the user is captured in one component and then passed onto a child component which fetches the weather data (based on the location) and then eventually renders an icon indicating the current weather conditions.
I'm passing the longitude and latitude state as props to my WeatherWidget. Unfortunately, the WeatherWidget also receives the initial state null. How I can I avoid that?
Thank you for your help!
class GetGeolocation extends Component{
constructor(){
super();
this.state = {
lngt: null,
latd: null
}
}
componentDidMount(){
this.getLocation()
}
getLocation = () => {
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(position => {
this.setState({lngt: position.coords.longitude.toFixed(4)});
this.setState({latd:position.coords.latitude.toFixed(4)});
}
);
};
}
render(){
return (
<>
<WeatherWidget lngt = {this.state.lngt} latd = {this.state.latd} />
</>
)
}
class WeatherWidget extends Component{
constructor(props){
super(props);
this.state = {
weather:[]
}
}
componentWillReceiveProps(nextProps){
this.getWeather(nextProps)
}
getWeather = (location) => {
console.log(location)
// The console logs twice:
// First:
//{lngt: "-12.3456", latd: null}
//Then, the correct values:
//{lngt: "-12.3456", latd: "78,9999"}
}
Don't use componentWillReceiveProps, that will be deprecated in later versions of React.
But also, you can just setup conditional logic in your life-cycle methods to determine what code to execute.
componentWillReceiveProps(nextProps){
//condition says if both value are truthy then run code.
if(nextProps.lngt && nextProps.latd){
this.getWeather(nextProps)
}
}
You can also use componentDidUpdate()
componentDidUpdate(){
//condition says if both value are truthy then run code.
if(this.props.lngt && this.props.latd){
this.getWeather(this.props)
}
}
One option is to conditionally render in the parent component:
class GetGeolocation extends React.Component {
constructor(props) {
super(props);
this.state = {
lngt: null,
latd: null
};
}
componentDidMount() {
this.getLocation();
}
getLocation = () => {
// Simulate the network request
setTimeout(() => this.setState({ lngt: 100 }), 1000);
setTimeout(() => this.setState({ latd: 100 }), 1000);
};
render() {
const { lngt, latd } = this.state;
if (!lngt || !latd) return null;
return <WeatherWidget lngt={lngt} latd={latd} />;
}
}
class WeatherWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
weather: []
};
}
componentDidMount() {
this.getWeather(this.props);
}
getWeather = location => {
console.log(location);
};
render() {
return null;
}
}
I'm working on an environment that is basically set up with a Main Component like this:
class MainComponent extends Component {
constructor(props) {
super(props);
this.state = {
selectedValues: []
};
}
render() {
const { selectedValues } = this.state;
return (
// Other components
<SubComponent selectedValues = {selectedValues} />
// Other components
);
}
}
export default MainComponent;
And a Sub Component like this:
class SubComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isExporting: false,
selectedValues: props.selectedValues
};
}
performTask = () => {
this.setState({ isWorking: true });
const { selectedValues } = this.state;
console.log(`Selected Values: ${selectedValues}`);
fetch('/api/work', {
method: 'GET'
})
.then(res => res.json())
.then((result) => {
// Handle the result
this.setState({ isWorking: false });
})
.catch((error) => {
console.log(error);
this.setState({ isWorking: false });
});
};
render() {
const { isWorking } = this.state;
return (
<Button
bsStyle="primary"
disabled={isWorking}
onClick={() => this.performTask()}
>
{isWorking ? 'Working...' : 'Work'}
</Button>
);
}
}
SubComponent.propTypes = {
selectedValues: PropTypes.arrayOf(PropTypes.string)
};
SubComponent.defaultProps = {
selectedValues: []
};
export default SubComponent;
In the Main Component, there are other components at work that can change the selectedValues. The functionality I'd like to see is that when the performTask method fires, it has the most recent and up to date list of selectedValues. With my current setup, selectedValues is always an empty list. No matter how many values actually get selected in the Main Component, the list never seems to change in the Sub Component.
Is there a simple way to do this?
I would suggest you 2 of the following methods to check this problem:
Maybe the state.selectedItems doesn't change at all. You only declare it in the contractor but the value remains, since you didn't setState with other value to it. Maybe it will work if you will refer to this.props.selectedItems instead.
Try to add the function component WillReceiveProps(newProps) to the sub component and check the value there.
If this method doesn't call, it means the selectedItems doesnt change.
Update if some of it works.
Good luck.
selectedValues in SubComponent state has not updated since it was set in SubComponent constructor. You may need to call setState again in componentWillReceivedProps in SubComponent
I'm trying to redirect to the second page on button click. Yet, Im getting cannot read setState of undefined when working the code below.
this.state = {
arr: [],
redirect: false
};
setRedirect() {
this.setState({
redirect : true
})
}
renderRedirect(){
if (this.state.redirect) {
return <Redirect to='/target' />
}
}
render() {
return (
<div>
{this.renderRedirect()}
<button onClick={this.setRedirect}>Redirect</button>
</div>
)}
Add setState inside constructor or define as class property
constructor(props) {
super(props);
this.state = {
arr: [],
redirect: false
}
}
Or
class abc extends Component {
state = {
arr: [],
redirect: false,
}
}
Also
Change function to es6 or use bind
ES6 Solution
setRedirect = () => {
// Code block
}
Constructor
this.state = {
arr: [],
redirect: false
}
function that toggle redirect flag outside constructor
setRedirect=()=>{
this.setState({redirect:true})
}
finally you render method
render() {
return (
<div>
{this.renderRedirect()}
<button onClick={this.setRedirect}>Redirect</button>
</div>
)
}
I think here is issue of binding this if you haven't written setState in constructor. If so then
there are threee ways of changes this so that you setRedirect can have access to this of whole class. (so that it can access state).
binding in render like onClick={()=>this.setRedirect.bind(this)}
binding in constructor like
this.setRedirect = this.setRedirect.bind(this)
last on considering performance the above menthod setRedirect =()=>{}
Because 1,2 creates new function at every render.
You need to bind the setDirect function like this
class App extends React.Component {
state = {
arr: [],
redirect: false
};
setRedirect() {
this.setState({
redirect: true
})
}
renderRedirect(){
if (this.state.redirect) {
return <Redirect to='/target'/>
}
}
render() {
return (
<div>
{this.renderRedirect()}
<button onClick={this.setRedirect.bind(this)}>Redirect</button> //binding here is important
</div>
)
}
}
or you have to use the constructor of class
constructor(props) {
super(props);
this.state = {
arr: [],
redirect: false
}
}
You need to move state inside constructor and bind setRedirect in constructor only.
Few Other answers suggested to bind it directly in render don’t do that. Bind it always in constructor
To fix the issue primarily you need to bind setRedirect function
constructor(props){
super(props);
this.state = {
arr: [],
redirect: false
};
this.setRedirect = this.setRedirect.bind(this);
}
Also Change
<button onClick={this.setRedirect}>Redirect</button>
To
<button onClick={() => this.setRedirect()}>Redirect</button>
Otherwise setRedirect gets called on render so to prevent that use () =>
I am new with reactjs.
This is what I am trying
class EventDemo extends Component {
constructor(){
super()
this.getStarWars()
this.state = {}
}
getStarWars = ()=> axios.get('https://swapi.co/api/people')
.then(res => {
console.log(res.data)
this.setState({
names: res.data.results
})
})
render() {
console.log(this.state.names);
return (
<div>
{this.state.names.map(function(e){
return <li>{e.name}</li>
})}
</div>
);
}
}
But This following error i am getting
What I am doing wrong here ? It supposed to work .
First of all,you shouldn't call your this.getStarWars() function inside the constructor, it is a very bad practice and could cause you troubles, http calls in React component should be generally called from the componentDidMount function.
However the issue in this case is another one,you haven't given an initial value to this.state.names, so when the component tries to do the initial render it fails because the names are undefined since the initial render appens before the http call is resolved
You code should be fixed like this:
class EventDemo extends Component {
constructor(){
super()
this.state = { names:[] }
}
componentDidMount(){
this.getStarWars()
}
getStarWars = ()=> axios.get('https://swapi.co/api/people')
.then(res => {
console.log(res.data)
this.setState({
names: res.data.results
})
})
render() {
console.log(this.state.names);
return (
<div>
{this.state.names.map(function(e){
return <li>{e.name}</li>
})}
</div>
);
}
}