React : How to avoid multiple function call - javascript

I am working on small react project, actually I have some problem regarding to function call. I am updating the value of URL and invoke the method through button click to display updated values but whenever I click the button first it give me old values when I click again it give me updated values. Could someone please help me how to avoid old values on first click, I want to show updated values on first click not on second click. I am new to ReactJS , Could someone please help me how to fix this problem?
Full Component Code
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
Item: 5,
skip: 0
}
this.handleClick = this.handleClick.bind(this);
}
urlParams() {
return `http://localhost:3001/meetups?filter[limit]=${(this.state.Item)}&&filter[skip]=${this.state.skip}`
}
handleClick() {
this.setState({skip: this.state.skip + 1})
}
render() {
return (
<div>
<a href={this.urlParams()}>Example link</a>
<pre>{this.urlParams()}</pre>
<button onClick={this.handleClick}>Change link</button>
</div>
)
}
}
ReactDOM.render(<Example/>, document.querySelector('div#my-example' ))

State is not updating at time when urlParams is being called. As #jolly told you setState is Asynch function so it's better to use it in a callback or you may simply pass your sortedData type as in argument in getSortedType function instead of updating state. Also, ReactJS community suggests or the best practice is to use state only if props are needed to be updated.
In case, if you dont feel comfortable with CB. you may use setTimeOut which I think is a bad practice but it would solve your problem. your code would be like:
getSortedData=()=>{
this.setState({sortedData:'name desc'});
setTimeout(() => {
this.getData();
}, 200);
}

There's quite lots of code e no working example, so I may be wrong, but I think the problem is in getSortedData(): in that function, you call setState() and right after you call getData() which will perform a fetch, which uses ulrParams() function, which uses property this.state.sortedData.
The problem in this is that setState() is async, so it's not sure that, when urlParams() uses this.state.sortedData, the property is updated (actually, we are sure it's not updated).
Try to rewrite getSortedData() as follow:
getSortedData =() => {
this.setState({sortedData:'name desc'}, () => {
this.getData();
})
}
What I'm doing here is passing a callback to setState(): doing this way, getData() will be called AFTER you've updated the state.
Regarding the question you asked in the comment, if you want to toggle the order, you can add a property order in the state of the Component, assigning to it a default value 'name desc'.
Then, you change getSortedData as follow:
getSortedData =() => {
let newSort = 'name asc';
if (this.state.sorteData === newSort) 'name desc';
this.setState({sortedData: newSort}, () => {
this.getData();
})
}

Related

How to check that a certain prop has received?

I have an synchronization issue. I want to check props is loaded or not to child component.
I have wrote a code like that :
componentDidMount() {
setTimeout(() => {
this.setState({
editorContent: this.props.json[this.state.contentType],
});
}, 2500);
}
Here I'm using 'setTimeout' function. In render function I'm using 'editorContent' state so this is undefined if I don't use 'setTimeout'. After 1 or 2 sec. props received and it won't be undefined. I guess there is better solution for that.
Thanks all
This method is completely not sustainable instead you should use useEffect let me demonstrate by code in functional component.
//cildComponent.js
export default ChildComponent(props){
const [pending , setPending] = useState(true)
useEffect(()=>{
if(props.name){
// now you have the name available
setPending(false);
}
},[props])
if(pending){
return “loading”
}
return ‘your name is ${props.name}’
}

Passing props to a state to use in a class component

Will preface that I have about 4 weeks of coding experience and so apologies in advance if I'm missing something obvious.
I'm trying to pass an array of objects from Profile.js to Calendar.js, with Calendar.js currently ultimately being returned again in Profile.js. The idea's that a user would be able to set a schedule and be able to see the same schedule when they go back to the profile page. I was trying to get this to work by having the saved array of objects be displayed upon rendering the Calendar.js, but right now it basically resets itself every time the user goes back to the page.
Two issues I'm guessing are relevant here:
Initializing this.state to this.props.calendar isn't working (seems like this.props.calendar returns empty during initialization) but when I log this.props.calendar in the handleChange or render() it does show up correctly.
I believe handleChange runs whenever the page is clicked and so was thinking there might need to be some way for Calendar.js to display the old saved array of objects instead of an empty array (in case no schedule is selected at the time of click). Was trying to implement some kind of conditional operator here but handleChange currently stops working if it is called while a new empty schedule's selected (so if the user happens to click on the page before selecting a schedule).
This came out to be a bit longer than I expected, but any help would be greatly appreciated!
Calendar.js:
export default class Calendar extends React.Component {
constructor(props) {
super(props);
this.state = {
schedule: this.props.calendar
};
}
handleChange = newSchedule => {
this.setState({ schedule: newSchedule.length !==0 ? newSchedule : this.props.calendar });
this.props.handleSubmitCalendar(newSchedule); // call API function to save current selected schedule to a database
}
render() {
return (
<ScheduleSelector
selection={this.state.schedule}
...code...
/>
)
}
}
Profile.js:
import Calendar from "../components/Calendar";
export default function Profile() {
const [calendar, setCalendar] = useState([]);
useEffect(() => {
async function onLoad() {
try {
const profileCalendar = await loadCalendar();
setCalendar(profileCalendar);
} catch (e) {
onError(e);
}
}
onLoad();
}, []);
async function handleSubmitCalendar(calendar) {
setIsLoading(true);
try {
updateCalendar(calendar);
setIsLoading(false);
} catch (e) {
onError(e);
setIsLoading(false);
}
}
function loadCalendar() {
return API.get("notes", "/calendar");
}
function updateCalendar(calendar) {
return API.put("notes", "/calendar", {
body: {calendar}
});
}
return (
<div className="Calendar">
<Calendar calendar={calendar} handleSubmitCalendar={handleSubmitCalendar}/>
</div>
);
}
Welcome! You should really read the React documentation and go through the examples. Everything you need to known to answer this question is in there and going through it all will make you a more competent developer and save you a lot of headaches. Available here. If you still don't understand once you've gone through the docs, ask again here and I will help you.

React re-render conditional on state change without changing the conditional

I've got a conditional that displays an editor while a certain prop remains true. The thing is, the data with which that editor is rendered with should change every time I select another object with which to populate that editor.
However, because the prop responsible for the conditional rendering doesn't change, even though the data with which the editor is rendered does, it refuses to re-render on state change.
I'm not particularly good at React, so, hopefully someone can explain how I can get around this little hiccup.
Conditional render
{this.state.showEditor ? (<BlockEditor routine={this.state.editorObject} />) : null}
Method that is being called.
handleShowEditor = routine => {
this.setState({ showEditor: true });
this.setState({ editorObject: routine });
};
The editor component
export default class BlockEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
routine: this.props.routine
};
}
render() {
return (
<div>
<Editor
autofocus
holderId="editorjs-container"
onChange={data => this.handleSave(data)}
customTools={{}}
onReady={() => console.log("Start!")}
data={this.props.routine.description}
instanceRef={instance => (this.editorInstance = instance)}
/>
</div>
);
}
}
Is there a reason for setting state separately? Why not
handleShowEditor = routine => {
this.setState({
showEditor: true,
editorObject: routine
});
};
Keep in mind that setState is asynchronous and your implementation could lead to such weird behaviour.
If you are still looking for an answer i have faced the same problem working with the same [Editor.JS][1] :).
This worked for me with functional component:
// on change fires when component re-intialize
onChange={async (e) => {
const newData = await e.saver.save();
setEditorData((prevData) => {
console.log(prevData.blocks);
console.log(newData.blocks);
if (
JSON.stringify(prevData.blocks) === JSON.stringify(newData.blocks)
) {
console.log("no data changed");
return prevData;
} else {
console.log("data changed");
return newData;
}
});
}}
// setting true to re-render when currentPage data change
enableReInitialize={true}
Here we are just checking if data changes assign it to editorData component state and perform re-render else assign prevData as it is which will not cause re-render.
Hope it helps.
Edit:
i am comparing editor data blocks change which is array.
of course you need to perform comparison of blocks more deeply than what i am doing, you can use lodash for example.
[1]: https://github.com/editor-js/awesome-editorjs
As setState is asynchronous you can make another call in its callback.
Try like this
handleShowEditor = routine => {
this.setState({
showEditor: true
}, () =>{
this.setState({
editorObject: routine
)}
});
};

React setState re-render

First of all, I'm really new into React, so forgive my lack of knowledge about the subject.
As far as I know, when you setState a new value, it renders again the view (or parts of it that needs re-render).
I've got something like this, and I would like to know if it's a good practice or not, how could I solve this kind of issues to improve, etc.
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = {
key: value
}
this.functionRender = this.functionRender.bind(this)
this.changeValue = this.changeValue.bind(this)
}
functionRender = () => {
if(someParams !== null) {
return <AnotherComponent param={this.state.key} />
}
else {
return "<span>Loading</span>"
}
}
changeValue = (newValue) => {
this.setState({
key: newValue
})
}
render() {
return (<div>... {this.functionRender()} ... <span onClick={() => this.changeValue(otherValue)}>Click me</span></div>)
}
}
Another component
class AnotherComponent extends Component {
constructor (props) {
super(props)
}
render () {
return (
if (this.props.param === someOptions) {
return <div>Options 1</div>
} else {
return <div>Options 2</div>
}
)
}
}
The intention of the code is that when I click on the span it will change the key of the state, and then the component <AnotherComponent /> should change because of its parameter.
I assured that when I make the setState, on the callback I throw a console log with the new value, and it's setted correctly, but the AnotherComponent doesn't updates, because depending on the param given it shows one thing or another.
Maybe I need to use some lifecycle of the MyComponent?
Edit
I found that the param that AnotherComponent is receiving it does not changes, it's always the same one.
I would suggest that you'll first test it in the parent using a simple console.log on your changeValue function:
changeValue = (newValue) => {
console.log('newValue before', newValue);
this.setState({
key: newValue
}, ()=> console.log('newValue after', this.state.key))
}
setState can accept a callback that will be invoked after the state actually changed (remember that setState is async).
Since we can't see the entire component it's hard to understand what actually goes on there.
I suspect that the newValue parameter is always the same but i can't be sure.
It seems like you're missing the props in AnotherComponent's constructor. it should be:
constructor (props) {
super(props) // here
}
Try replacing the if statement with:
{this.props.param === someOptions? <div>Options 1</div>: <div>Options 2</div>}
also add this function to see if the new props actually get to the component:
componentWillReceiveProps(newProps){
console.log(newProps);
}
and check for the type of param and someOptions since you're (rightfully) using the === comparison.
First, fat arrow ( => ) autobind methods so you do not need to bind it in the constructor, second re-renders occur if you change the key of the component.
Ref: https://reactjs.org/docs/lists-and-keys.html#keys

Is there a synchronous alternative of setState() in Reactjs

According to the explaination in the docs:
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
So since setState() is asyncronous and there is no guarantee about its synchronous performance. Is there an alternative of setState() that is syncronous.
For example
//initial value of cnt:0
this.setState({cnt:this.state.cnt+1})
alert(this.state.cnt); //alert value:0
Since the alert value is previous value so what is the alternative that will give alert value:1 using setState().
There are few questions on Stackoverflow which is similar to this question but no where I am able to find the correct answer.
As you have read from the documentation, there is NO sync alternative, reason as described is performance gains.
However I presume you want to perform an action after you have changed your state, you can achieve this via:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
x: 1
};
console.log('initial state', this.state);
}
updateState = () => {
console.log('changing state');
this.setState({
x: 2
},() => { console.log('new state', this.state); })
}
render() {
return (
<div>
<button onClick={this.updateState}>Change state</button>
</div>
);
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
You could wrap setState in a function returning a promise, and then use this function with the await keyword to cause your code to wait until the state has been applied.
Personally, I would never do this in real code, instead I would just put the code I wish to execute after the state update in the setState callback.
Nevertheless, here is an example.
class MyComponent extends React.Component {
function setStateSynchronous(stateUpdate) {
return new Promise(resolve => {
this.setState(stateUpdate, () => resolve());
});
}
async function foo() {
// state.count has value of 0
await setStateSynchronous(state => ({count: state.count+1}));
// execution will only resume here once state has been applied
console.log(this.state.count); // output will be 1
}
}
In the foo function, the await keyword causes the code execution to pause until the promise returned by setStateSynchronous has been resolved, which only happens once the callback passed to setState is called, which only happens when the state has been applied. So execution only reaches the console.log call once the state update has been applied.
docs for async/await:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
If this is required I would suggest using a callback in your setState function (and I also suggest using a functional setState).
The callback will be called after the state has been updated.
For example, your example would be
//initial value of cnt:0
this.setState(
(state) => ({cnt: state.cnt+1}),
() => { alert(this.state.cnt)}
)
as per documentation here : https://facebook.github.io/react/docs/react-component.html#setstate
Note: Official docs say, "Generally we recommend using componentDidUpdate() for such logic instead."
No, there is not. React will update the state when it sees fit, doing things such as batching setState calls together for efficiency. It may interest you that you are able to pass a function into setState instead, which takes the previous state, so you may choose your new state with good knowledge of the previous one.
You can use flushSync from ReactDOM to update the state synchronously as suggested by react docs.
https://reactjs.org/docs/react-component.html
It may sound weird but yes setState can work synchronously in react.
How so? This is POC which I've created to demonstrate it.
Pasting the only app JS code.
Maybe it's possible that I'm missing something but this was actually happening in my application that's when I came to know about this effect.
Correct me if this kind of behavior is expected in React which I'm unaware of.
When there are multiple setState on main thread the setState runs a Batch combining all the setState on the main method. Whereas the Scenario is different when the same things go inside the async Function.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
this.asyncMethod = this.asyncMethod.bind(this);
this.syncMethod = this.syncMethod.bind(this);
}
asyncMethod() {
console.log("*************************")
console.log("This is a async Method ..!!")
this.setState({
counter: this.state.counter + 1
}, () => {
console.log("This is a async Method callback of setState. value of counter is---", this.state.counter);
})
console.log("This is a async Method on main thread. value of counter is---", this.state.counter);
console.log("*************************")
}
syncMethod() {
var that = this;
console.log("*************************")
console.log("This is a sync Method ..!!")
that.setState({counter: "This value will never be seen or printed and render will not be called"});
that.setState({counter: "This is the value which will be seen in render and render will be called"});
setTimeout(() => {
that.setState({counter: "This is part is synchronous. Inside the async function after this render will be called"});
console.log("setTimeout setState");
that.setState({counter: "This is part is aslso synchronous. Inside the async function after this render will be called"});
}, 10)
console.log("This is a sync Method on Main thread. value of counter is---", this.state.counter);
console.log("*************************")
}
render() {
console.log("Render..!!",this.state.counter);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
</header>
<button onClick={this.asyncMethod}>AsyncMethod</button>
<button onClick={this.syncMethod}>SyncMethod</button>
</div>
);
}
}
export default App;
Use React Hooks instead:
function MyComponent() {
const [cnt, setCnt] = useState(0)
const updateState = () => {
setCnt(cnt + 1)
}
useEffect(() => {
console.log('new state', cnt)
}, [cnt])
return (
<div>
<button onClick={updateState}>Change state</button>
</div>
)
}
I was able to trick React into calling setState synchronously by wrapping my code in setTimeout(() => {......this.setState({ ... });....}, 0);. Since setTimeout puts stuff at the end of the JavaScript event queue, I think React detects the setState is within it and knows it can't rely on a batched setState call (which would get added to the end of the queue).
In functional components I do this:
const handleUpdateCountry(newCountry) {
setIsFetching(() => true);
setCompanyLocation(() => newCountry);
setIsFetching(() => false);
}
Correct me if I'm wrong but as far as I know this is synchronous then and it also just worked in my situation.
Yes, there is a method with which we can make our synchronous setState. But its performance maybe not good as normally
For example, we have
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: 0
};
}
changeState(){
console.log('in change state',this.state.data);
this.state.data = 'new value here'
this.setState({});
console.log('in change state after state change',this.state.data);
}
render() {
return (
<div>
<p>{this.state.data}</p>
<a onClick={this.changeState}>Change state</a>
</div>
);
}
}
In this example, we change the state first and then render our component.
Short answer to your question is - NO, react doesn't have sync method setState.
In some cases, an alternative is using refs (createRef or useRef) instead of states.
//initial value of cnt:0
const cnt = React.createRef(0); //or React.useRef(0);
cnt.current++;
alert(cnt.current); //output: 1
So far the best solution for me is to use the callback function.
this.setState({cnt:this.state.cnt+1},() =>{
// other task which we want to run synchronously
alert(this.state.cnt);
})
according to the official documentation of react, we can make setState synchronous by passing the callback function in the second argument.

Categories

Resources