I am brand new to Reacjs, I visited several question with a similar title but none helped me.
Why this code doesn't change my state?
componentWillMount()
{
/** I am using superagent to fetch some data but it doesn't matter */
var url = "http://www.omdbapi.com/?s=star&apikey=mykey";
Request.get(url).then((response) => {
this.setState({ messages: response.body.Search});
});
}
My render method
render() {
return (
<div>
<ListaMensagens messages={this.state.messages} /> this.state is null here.
</div>
...
How can I change my state with the retrieved data and pass it to a child component?
In general you can use setState in componentWillMount without getting into trouble, BUT...
in your case you are setting the state after the response of the request which is causing problems.
Three solutions for your problem:
1: Use the constructor to initialize the state.
constructor(props){
super(props);
this.state = {
messages: []
};
}
2: Fire setState in componentWillMount without waiting for any Promise
componentWillMount() {
this.setState({
messages: []
});
request(...);
}
3: In the render function check if the state is set properly
render(){
<div>
{ this.state && this.state.messages && <ListMensagens ... /> }
</div>
}
Related
I am new to react and getting confused between react hooks. There are many similar questions asked and I tried a few answers but it didn't work. I am trying to use a value of flag which has been set in componentDidmount() in render(). But I am getting undefined. Here is my code. Can someone help me?
export default class Shop extends Component {
constructor(props) {
super(props);
this.state = {
isContentTypeShop1: false,
};
}
async componentDidMount() {
const basketContextData = await fetchBasketContext(); // returns json object
const basketContentType = basketContextData.basketContentType; //returns string 'shop1'
console.log(basketContentType)
if(basketContentType === 'shop1') {
this.isContentTypeShop1 = true;
}
console.log(this.isContentTypeShop1); // returns true
}
render() {
console.log(this.isContentTypeShop1); //returns undefined
return (
<ul className="progress-bar">
<li>
{(this.isContentTypeShop1) && ( // hence doesn't work
<span>
Shop 1
</span>
)}
</li>
</ul>
);
}
}
You need to make use of setState to trigger a re-render from componentDidMount. Also isContentTypeShop1 isn't a class variable but its a state
async componentDidMount() {
const basketContextData = await fetchBasketContext(); // returns json object
const basketContentType = basketContextData.basketContentType; //returns string 'shop1'
console.log(basketContentType)
if(basketContentType === 'shop1') {
this.setState({isContentTypeShop1: true});
}
}
render() {
// use it from state
console.log(this.state.isContentTypeShop1);
}
this.isContentTypeShop1 doesn't exist because isContentTypeShop1 is inside state. Try this instead:
console.log(this.state.isContentTypeShop1);
And to update isContentTypeShop1, you need to call setState:
this.setState({ isContentTypeShop1: true });
You need to use this.state.isContentTypeShop1 instead of this.isContentTypeShop1 & you can't set state using =
You need to use setState like this.setState({ isContentTypeShop1: true })
You need to read Using State Correctly part from the React docs
And for some additional reading :)
This app is supposed to filter words by a specific input. I want to call a function with setState() when rendering a component and technically it's working but there is warning in the console.
Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
I guess that this is because I'm calling the function in the render function which I shouldn't, but what should I do instead?
class UsersList extends React.Component {
constructor(props) {
super(props);
this.state = {
allUsers: ["Michał", "Ania", "Kasia", "Tomek", "Hubert", "Jan", "Martyna", "Rafał", "Bartłomiej"],
filteredUsers: [],
input: null
}
}
filter() {
if (this.state.input !== this.props.inputValue) {
const filtered = this.state.allUsers.filter(user => user.toLowerCase().includes(this.props.inputValue));
this.setState({
filteredUsers: filtered.map(user => <li key={user}>{user}</li>),
input: this.props.inputValue
})
}
return this.state.filteredUsers;
}
render() {
this.filter()
return (
<ul>
{this.state.filteredUsers}
</ul>
)
}
}
class App extends React.Component {
constructor() {
super();
this.state = {input: ""};
this.handleInput = this.handleInput.bind(this);
}
handleInput(e) {
this.setState({input: e.target.value})
}
render() {
return (
<div>
<input onChange={this.handleInput} type="search"/>
<UsersList inputValue={this.state.input} />
</div>
);
}
}
The issue here is caused by changes being made to your component's state during rendering.
You should avoid setting component state directly during a components render() function (this is happening when you call filter() during your component's render() function).
Instead, consider updating the state of your component only as needed (ie when the inputValue prop changes). The recommended way to update state when prop values change is via the getDerivedStateFromProps() component life cycle hook.
Here's an example of how you could make use of this hook for your component:
class UsersList extends React.Component {
constructor(props) {
super(props);
this.state = {
allUsers: ["Michał", "Ania", "Kasia", "Tomek",
"Hubert", "Jan", "Martyna", "Rafał",
"Bartłomiej"],
filteredUsers: [],
input: null
}
}
/* Add this life cycle hook, it replaces filter(). Props are updated/incoming
props, state is current state of component instance */
static getDerivedStateFromProps(props, state) {
// The condition for prop changes that trigger an update
if(state.input !== props.inputValue) {
const filtered = state.allUsers.filter(user => user.toLowerCase().includes(props.inputValue));
/* Return the new state object seeing props triggered an update */
return {
allUsers: state.allUsers
filteredUsers: filtered.map(user => <li key={user}>{user}</li>),
input: props.inputValue
}
}
/* No update needed */
return null;
}
render() {
return (<ul>{this.state.filteredUsers}</ul>)
}
}
Hope this helps
The error is coming up as it could create an endless loop inside the component. As render method is executed whenever the state is updated and your function this.filter is doing a state update. Now as the state updates, your render method triggers the function again.
Best way to do that would be in lifecycle methods or maintain the uses in the App and make UserList a dumb component by always passing the list of filtered users for it to display.
I'm currently experiencing problems with getting ImmutableJS and React to work properly. This is not using Redux, therefore I am updating state within the component.
The problem is after I update state, my getter properties are missing on the next rerender causing an Error. Why does setState strip these methods away?
Am I not using ImmutableJS correctly? Is ImmutableJS only intended to be with Redux?
const AssociateRoleLocationStateFactory = RecordFactory<AssociateRoleLocationState>({
isEditing: false
})
export default class IAssociateRoleLocation extends React.Component {
constructor(props) {
super(props)
this.state = new AssociateRoleLocationStateFactory({
isEditing: false
})
// `this.state` has `.get` method to retrieve properties
console.log(`initial state:`, this.state) // Record {_map: Map}
}
toggleEditing() {
let nextState = this.state.set('isEditing', !this.state.get('isEditing'))
// `nextState` has `.get` method to retrieve properties
console.log(`next state:`, nextState) // Record {_map: Map}
this.setState(nextState)
}
render() {
console.log(this.state) // {_map: Map} // Its missing `.get` method on second render
let isEditing = this.state.get('isEditing')
return (
<div className="site-single-column">
{isEditing ? (
<div>
Is Editing!!
</div>
) : (
<button style={{ alignSelf: 'center' }} onClick={this.toggleEditing}>
Toggle
</button>
)}
</div>
)
}
}
Currently, React only supports plain js object as the state. You can wrap whatever you want in another key/value layer like
constructor(props) {
super(props);
this.state = {
payload: new AssociateRoleLocationStateFactory({
isEditing: false
})
}
}
There are discussions around this going on like this: https://github.com/facebook/react/issues/3303
But before that, stay pure.
I am using axios for a React project, and I was wondering if the usage of then promise is correct in this case.
Basically, I use axios to fetch data from the database when the component renders.
class Participants extends React.Component{
constructor(props){
super(props);
this.state = {
databaseUsers: [],
}
this.getUsers = this.getUsers.bind(this);
}
getUsers(){
var users = axios.get('/users/get-users').then((response) => {
this.setState({databaseUsers: response.data});
});
}
componentWillMount(){
this.getUsers();
}
render(){
console.log(this.state.databaseUsers);
return(** html tree **);
}
}
What I observe is that the state of the component is set twice, once when the rendering occurs, and the then promise fires, and a second time when the promise is done fetching the data from the database and sets the state.
How do I get more control over this? Like actually wait for the data on the database, and then render?
Any tips are welcome.
There are other ways to implement what you did with several components.
But let's stick to this example.
There is nothing wrong to rendering twice, as you don't want to wait for the response and then display output.
You can have a loading flag so you could show a "loading" code and when loaded show the output.
Or you can have 1 parent component that manages the work:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
data: []
}
}
componentDidMount() {
this.setState({loading: true})
axios.get('/users/get-users').then((response) => {
this.setState({
loading: false,
data: response.data
})
});
}
render() {
if (this.state.loading) {
return <LoadingComponent />;
}
return <DataComponent data={this.state.data} />
}
}
I'm new to React. I'm stuck on this, would really appreciate some help!
A parent component will pass an array into this child component. When I console.log(this.props.repairs) it shows me an array of 4. I am trying to update this.state.sortedDataList whenever the array of repairs is passed in. The console.log(this.state) is still showing sortedDataList as an empty array.
What am I doing wrong? Thanks so much, appreciate any help.
class Repairs extends React.Component {
constructor(props) {
super(props);
this.state = {
sortedDataList: []
};
}
componentWillReceiveProps(nextProps) {
if(this.props != nextProps) {
this.setState({
sortedDataList: this.props.repairs
});
}
}
render() {
console.log(this.props);
console.log(this.state);
return (
<div></div>
);
}
}
Never mind, found my silly mistake! If anyone else gets stuck on this in the future...
componentWillReceiveProps(nextProps) {
if(this.props != nextProps) {
this.setState({
sortedDataList: nextProps.repairs
});
}
}
componentWillReceiveProps isn't called on the first render. That is the reason that you don't see any update in the state
From the React Docs:
"Invoked when a component is receiving new props. This method is not called for the initial render."
If you want to make the change only first time you can make use of componentWillMount lifecycle function and update the state. On subsequent changed you componentWillReceiveProps will be called.
class Repairs extends React.Component {
constructor(props) {
super(props);
this.state = {
sortedDataList: []
};
}
componentWillMount() {
this.setState({
sortedDataList: this.props.repairs
}, () => console.log(this.state.sortedDataList));
}
componentWillReceiveProps(nextProps) {
if(this.props != nextProps) {
this.setState({
sortedDataList: nextProps.repairs
});
}
}
render() {
console.log("r",this.props);
console.log("r",this.state);
return (
<div></div>
);
}
}
class App extends React.Component {
render() {
var arr = ["1", "2", "3"];
return (
<div >
<Repairs repairs={arr}/>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="app"></div>
In your constructor
this.state = {
sortedDataList: []
};
You initially set state to an empty array, so on first render it'll be empty. Then, whenever the props are changed, it'll get updated via componentWillReceiveProps().
As Shubham said, componentWillReceiveProps() isn't called on the first render. If you want the state to reflect the props right from the first render, you'd have to put it in the constructor like so:
this.state = {
sortedDataList: this.props.repair
};