I have axios get method which is called from Mobx store here's the code:
fetchPizzas=()=>{axios.get("http://localhost:3000/db.json").then((resp)=>{this.setPizzas(resp.data.pizzas);})}
In App.js i call this method with React useEffect like this:
React.useEffect(()=>pizzaStore.fetchPizzas(), []);
However, all the pizza items don't render unless you click any button on the page. I am replacing jsx files with tsx files if that matters.
You are updating the state of pizzaStore in your react.useEffect
as fetchPizzas is saving the state this.setPizzas and the 'this' context looks like pizzaStore,
I think once pizzaStore is updated, you should update the state of App so it re-renders.
is pizzaStore saved as state of App, in which case you may want something like:
componentDidMount(){
pizzaStore.fetchPizzas();
}
// runs when pizzaStore is updated.
React.useEffect(()=> {
setPizzaStore(pizzaStore)
, [pizzaStore]);
Related
I am using React Context Provider and Consumer in order to pass data from my App component to its children.
My Context is called CurrentUserContext. I declare it like this:
const CurrentUserContext = React.createContext();
and export it like this:
export default CurrentUserContext;
I am wrapping my App component with CurrentUserContext.Provider and passing as the value a stateful data I declare like this:
const [loggedUser, setLoggedUser] = useState(null);
When I want a child component to use the data, I wrap it with CurrentUserContext.Consumer.
It all works perfectly fine, but for some reason, when I update loggedUser using setLoggedUser (in useEffect for example), the consumers does not get re-rendered.
Does anyone know why?
I want every time that I update loggedUser using setLoggedUser, the relevant components (the consumers) will re-render.
Does anyone know what doesn't it work?
Thank you!
This might be a really stupid question but I am writing my first project in React and am struggling to understand the purpose of setState hooks.
As far as I understand, the setState hook is used to set current values used in a component that is scoped to that component only, and does not persist if a page is reloaded for example, the value is simply held in memory until it is destroyed.
My question is, what is the difference between using setState() to store values and just simply declaring a let variable and updating it the regular way? Both methods just seem to be holding a non-persisting value scoped to that component. What is the difference?
changes in the state automatically cause your app to re-render (in most cases), so typically you store data in a state that is being displayed and possibly changed throughout the app (a menu whose options can change based on previous selections, for example).
TL;DR, even though the answer's not very long:
setState or useState is the key for React to subscribe to your component's state and update your components when necessary. Using let variables for storing app state means React will never get to know about state change and won't rerender and update your components.
A short overview of React
The core principle of React is that your components, and consequentially your UI, are a function of your app's state. When your app's state changes, components "react" to this state change and get updated. Here's a simple example:
const CounterButton = () => {
// Create a state variable for counting number of clicks
const [count, setCount] = React.useState(0);
// Decide what the component looks like, as a function of this state
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
};
ReactDOM.render(<CounterButton />, document.querySelector('#root'));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
This is a component that just creates a button that shows how many times it has been clicked. Now, the component needs to store information about how many times it has been clicked - this means that the clicks count is a part of this component's "state". That's what we get from React.useState(0) - a state variable whose initial value is 0, and a function that allows us to change the value. Whenever you call setCount with some value, React gets to know that the CounterButton component's state has changed and thus the CounterButton component needs a rerender.
So in other words, React allows you to neatly and concisely define what internal state a component requires and what the component looks like as a function of this internal state (and external props). React does rest of the work - it manages the app's state, and whenever a piece of state changes anywhere in the app, React updates the components that depend on that. In other words, components "react" to data change.
To your question
If you use a simple let variable instead of React.useState in the above example, React will no longer get to know if the count variable has changed. useState is the key for React to "subscribe" to your component's state.
const CounterButton = () => {
// React will never get to know about this variable
let count = 0;
return (
<button onClick={() => count++}>
Count: {count}
</button>
);
};
In fact, for a functional component, let variables won't work in any case because while rendering a functional component, React internally runs the function. That would mean your let variable would be reset to its default value. The above reason is more relevant to class components. Using let variables to store component state is like hiding things from React - and that's not good because then there's no one else to rerender your component when component state changes :)
This part of the React docs is a bit relevant - it does not go into any details, though.
React re-renders the new / updated state on an event occurance storing value into a variable wont trigger re-render and data is passed on form parent component to child component through props and a change in state can be reflected among all the parts.
For example we need to print 100 elements on a page if an element is modified or updated in any way this triggers re-render but using var if the variable is modified or updated this won't cause re-render where in data wont be updated.
I'm contributing to an open-sourced React/Redux application built using ES6 JavaScript. I'm fairly new to React/Redux so I'm having some trouble.
I have a parent class that's rendering two different React components. The first component contains some input fields regarding events (called NewShift). The second component is a calendar that renders these events (called Index).
Once a user fills out the input fields and presses a button in the first component, I want to be able to re-render the calendar in the second component. If the re-render function is in the calendar component, how do I call it from the input fields component (both children).
React re-renders components whenever component state is changed. Should you be using just React, this would mean passing changed values up to the parent component's state to force a re-render.
However, Redux makes this easier on you, as it's 'Store' functions as a global state. You can force a re-render by changing appropriate variables within the store.
Given your situation: the button should get this within it's onClick attribute:
onClick={() => dispatchNewCalendarInfo(payload)}.
dispatchNewCalenderInfo should also be imported by the component:
import { dispatchNewCalendarInfo } from './redux/path/to/post/actions.js'; and connected to it: export default connect(()=>({}), { dispatchNewCalendarInfo })(Component);. Note, you need to also import connect from 'react-redux' for this.
And, of course, dispatchNewCalendarInfo should be present in the actions.js path, and accepted by the store reducer. This dispatch should alter information that the calendar is connected to, which will force it to update and re-paint.
If you're not using Redux there's another path you can take. Instead of having the function that takes input be in NewShift, have new shift receive the function as a prop from the parent.
So in your NewShift component you would have something like onClick={this.props.submitCalanderInfo()}
The submitCalanderInfo function would be part of the parent component. You would probably want this new info to be saved into the state of the parent component, and then then use that state to update the props on the calendar or Index component. So Index might look something like this:
<Index shiftData={this.state.shiftData} />
I am trying to understand how the new react context API works.
In redux, it is possible for a component to have knowledge of dispatch actions without knowing state. This allows updates to redux state without causing a rerender of components that don't care about that state.
For example I could have
<Updater onClick={updateCount}/>
and
<Consumer value={count}/>
Updater is connected to dispatch(updateCount()) and Consumer is connected to count's current value via state.count. When state.count is updated, only the Consumer rerenders. To me, that's a crucial behavior.
In react context, it seems very difficult to duplicate this behavior. I'd like to be able to update state without causing unnecessary rerenders of components that want to alter the context but don't actually care about the state.
How would it be possible for components to trigger updates to context if they are not inside a consumer? And I definitely don't want to trigger an update to the entire tree by setting state at the provider level.
interesting question. Not sure you can without at least an extra layer (but happy to be shown wrong).
Maybe using Memo or PureComponent to minimise the re-rendering?
import React, { memo } from 'react';
function Widget({ setContext }) {
return <button onClick={setContext}/>Click Me</button>;
}
export default memo(Widget);
...
function Wrap() {
const { setSession } = useContext(SessionContext);
return <Widget setSession={setSession} />;
}
One possible solution is to transform your consumer components into pure components and check against the values each component really cares about.
This can be easily done using the onlyUpdateForKeys HOC from recompose.
you can try this library react-hooks-in-callback to isolate the context from your component and pick only desired state values from it,
check this example
My TranslationDetail component is passed an id upon opening, and based on this an external api call is triggered in the class constructor, receiving data to the state, and this data being displayed on TranslationDetail.
//Routing:
<Route path="/translation/:id" component={TranslationDetail}/>
//Class:
class TranslationDetail extends Component {
constructor(props){
super(props);
this.props.fetchTrans(this.props.params.id);
}
This all works fine if I enter the url manually. In case I'd like to use react-router e.g. for displaying the next item like below the url does change, but the api call is not triggered, and the data will remain the same.
<button
type="button"
onClick={() =>
browserHistory.push(`/translation/${Number(this.props.params.id)+1}`)}>
Next
</button>
Please bear in mind that I'm a total beginner. The reason why this is happening is I believe that the constructor is run only once, thus no further api call is triggered.
How can I solve this?
Do I need to listed to props and call a function on change? If yes, how?
Constructor is not a right place to make API calls.
You need to use lifecycle events:
componentDidMount to run the initial fetch.
componentDidUpdate to make the subsequent calls.
Make sure to compare the props with the previous props in componentDidUpdate to avoid fetching if the specific prop you care about hasn't changed.
class TranslationDetail extends Component {
componentDidMount() {
this.fetchTrans();
}
componentDidUpdate(prevProps) {
if (prevProps.params.id !== this.props.params.id) {
this.fetchTrans();
}
}
fetchTrans() {
this.props.fetchTrans(this.props.params.id);
}
}
From React 16.3 and onwards componentWillMount, componentWillUpdate and componentWillReceiveProps are deprecated.
You can use static getDerivedStateFromProps and return a new state based on changes on props.
You don't have access to your this objects like props, so you cannot compare nextProps with your current props by nextProps.sth !== this.props.sth. You can compare you prevState value with nextProps and return new value of state.
Make sue you add UNSAFE_ to your current componentWillMount and the other deprecated lifecyle methods for now.
Use componentWillMount to get the data and set the state.
Then use componentWillReceiveProps for capturing update on the props.
You can check the Component Specs and Lifecycle.
I would use the render method. If the data is not loaded I would render a loader spinner and throw the action that fetch de data. For that i usually use the stores. Once the store has de data from the api, mark the data as loaded, throw an event and let the component get the data from the store, replacing the loader spinner with your data representation.