I have two Components of the same level (no owner-ownee relation). Suppose by clicking "C" in Component A, I set the temperature unit to "C", and clicking "F", I set the unit to "F". In Component B, I need to display the temperature, so I need to get the unit. But that information is in A. I wonder how I can access the state in Component A?
Follow up:
In another Component C, which is also parallel to A and B, I have a link to another page:
<div className="bottom_link">
<Link to={'weather-stations/eaB1rJytnpS5mZ2m'}>view detail</Link>
</div>
I also need to pass the unit info to that page. I wonder what is the recommended way to do this? Thanks.
Have all of your components, A, B and C, owned by component X and move the state there. Have a handler in component X that you pass into the children, and that handler will do a setStateon X.
An alternative is to use the Flux approach. Here you would use a store and have each component read from that shared store's state. The change from F to C and back would be done via an action creator and store handler.
The second approach is an extrapolation of the first: you are essentially 'lifting' the state up into the global scope with a store, whereas you are lifting it into X's scope by using X's state.
In general in React, you want your components to be as stateless as possible. Therefore I would recommend using the first approach. You can then lift the state higher if necessary (say X, Y and Z need to share the state) and A, B and C are left untouched.
Using props rather than state has the nice side effect of forcing you to think about the API of your components. Exactly what data does this component require to do its job? Then those requirements are passed as props.
If I were to give any single piece of advice to someone starting in React, it would simply to not use state whenever possible. Even when you think you need it, you can often remove it, so you should be trying as hard as possible to avoid state.
I'm working on an app with hundreds and hundreds of components, and I can barely think of a place where we use this.setState
The best way to do this now is with the useContext hook. Create a new file that can be called TemperatureContext.js. In there you will need the following:
import {createContext} from 'react'
export const Temperature = createContext(null);
//you can replace null with an initial condition
Then, in your app.js file import the file you made and create a useState value. Then, wrap both Component A and Component B in a provider tag:
import React, {useState} from 'react'
import{Temperature} from '.TemperatureContext.js'
function App(){
const [temperature, setTemperature] = useState();
return(
<Temperature.Provider value={{temperature, setTemperature}}>
<ComponentA>
<ComponentB
</Temperature.Provider>
)}
Then, in each of your components, you can import the temperature state by
import React, {useContext} from 'react';
import{Temperature} from '.TemperatureContext.js';
function ComponentA(){
const [temperature, setTemperature] = useContext(Temperature);
const tempChange(entry)=>{
...
setTemperature(entry)
}
return(
...
)
}
Now, by using the tempChange function in ComponentA, it will alter the state in everything that is wrapped within the Provider tags. Thankfully, react has advanced a long way since this question was posted.
Related
I've seen a few questions related to this topic, but none that tackle the issue head-on in a pure way. useContext is a great tool for minimizing prop-drilling and centralizing key data needed across your app; however, it comes at a cost that I'm trying to minimize.
The closest issue to the one I'm describing here was asked two years ago here. The question didn't gain a lot of traction and the only answer basically says call the context in a parent container and pass values down through props (defeats the purpose of context) or use Redux. Maybe that's the only way, but I wanted to bring the question back to the collective to see if there is a better answer that's not dependent on external libraries.
I've set up a code sandbox here to illustrate the issue. Re-renders are console logged out to make seeing them easier.
In short, when a state changes in context, every app accessing data from that context re-renders, even if it's not utilizing the state data that changed (because it's all ultimately passed through the value object). React.Memo does not work on values accessed from context the same way it does for properties.
For example, in the code sandbox linked above, in App.js if you comment out <AppContainerWithContext /> and load <AppContainer />, there are two states managed in the container, one called titleText and the other called paragraphText. titleText is passed as a prop to component called TitleText and paragraphText is passed to a component called ParagraphText. Both components are wrapped in React.memo(). There are two buttons called in the AppContainer and each has a function that changes the text back and forth based on the value of separate boolean states.
Here is the function that toggles the titleText, the one for paragraph text is the same logic:
const changeTitleHandler = useCallback(() => {
const title = listTitleToggle ? "Title B" : "Title A";
setListTitleToggle((pV) => !pV)
setTitleText(title);
}, [listTitleToggle]);
Since the titleText component and paragraphText components are wrapped with React.useMemo, they only re-render when the corresponding value passed to them changes. Perfect.
Now in App.js if you comment out the <AppContainer /> component and enable the <AppContainerWithContext /> component, the rendered output and result of button clicks is identical; however, the states that change and are rendered on the screen are now managed by AppContext (called contextTitleText and contextParagraphText and passed to the TitleText component and ParagraphText component via useContext.
Now, if you click on the button to toggle the title, the ParagraphText component re-renders too, even though it doesn't use the contextTitleText state. My understanding of why this happens is because the value object changes when the contextTitleText is updated, causing any component accessing that value object through useContext to re-render.
My question is this:
Is there a way to utilize useContext without causing re-renders on all components accessing the context. In the example above, can we utilize useContext to manage the contextTitleText and the contextParagraphText but only re-render the components where the state from context being accessed changes?
I'm working on a web application and I'm using the React Context without using the useReducer() hook. This is a simple example of how I'm using the Context in my app:
const [stateValue, setStateValue] = useState("");
const [stateValue1, setStateValue1] = useState("");
const contextValue : MainContext = {
stateValue: stateValue,
setStateValue: setStateValue,
stateValue1: stateValue1,
setStateValue1: setStateValue1
}
So I pass to my Context Provider the contextValue and every time a child component has to change the stateValuex just calls the setStateValuex so that it triggers the re-rendering of the stateValuex inside all the child components.
What would the pros and cons be on using instead the Context with the useReducer() hook?
I'd approach it as two issues: 1) pros/cons of useState vs useReducer 2) pros/cons of props vs context. Then stick those answers together.
useReducer can be useful if you have a complicated state that you want to make sure all your update logic is in one centralized location. useState on the other hand is good for simple state where you don't need that kind of control.
props is the standard way to pass values from one component to its child. If you're passing it a short distances, this is the simplest and best approach. context is useful if you need to pass values a long way down the component tree. If you have a lot of cases where a component receives a prop not for itself, but just so it can forward it to a child, then this may indicate context would be better than props.
const contextValue : MainContext = {
stateValue: stateValue,
setStateValue: setStateValue,
stateValue1: stateValue1,
setStateValue1: setStateValue1
}
P.S: if your context value is an object, don't forget to memoize it. If you don't, you'll be creating a brand new object every time you render, and that will force any components consuming the context to render too
const contextValue: MainContext = useMemo(() => {
return {
stateValue: stateValue,
setStateValue: setStateValue,
stateValue1: stateValue1,
setStateValue1: setStateValue1
}
}, [stateValue, stateValue1])
when you use hooks or custom hooks, the states from them are indivisual.
which means suppose you used useReducer in Component A and B. state from useReducer in A, B is totally different whereas if you use contextAPI the state is same.
I have a question that what is the difference between use getState from store directly or use mapStateToProps. Please look at me example below
import React, { Component } from 'react'
import store from '../store'
import { connect } from 'react-redux';
class Test extends Component {
constructor(props) {
super(props);
}
render() {
return (
<p>
<h1>{this.props.count}</h1>
<h2>{store.getState().reducer1.count}</h2>
</p>
)
}
}
const mapStateToProps = (state) => ({
count: state.reducer1.count
});
// export default Test;
export default connect(mapStateToProps)(Test);
Both store.getState and mapStateToProps above work normally, it still updates when state change. If we just use getState only, we don't need to use connect method.
Another point I've recognized is when use mapStateToProps with connect, in reducer we must return a new copy of object state than return that state with modification. If not, component will not update when state changed. Like this:
return Object.assign({}, state, {
count: state.count + 1,
payload: action.payload,
});
But if we use store.getState(), we can either return a new copy or the revised one. Like this:
state.count++;
state.payload = action.payload;
return state
Anyone know please explain to me, thank you.
P/S: and similar with store.dispatch vs mapDispatchToProps, those 2 will work normally, just want to know why we should use mapToProps with connect instead of call the function directly from the store.
mapStateToProps is just a helper function which is really helpful to manage the project in modular style. For example, you can even place all the logic of connect in separate files and use where you want.
Suppose if you're working on a large scale application, then guess a sorts of properties nested there. Using connect you're actually modularizing project which is very helpful for developers who watch the project.
If you don't, you're writing several lines of code in single file.
A possible problem you'll face when using getState() or dispatch() directly. See this post for a little help to make it clear.
The key benefit using connect is that you don't need to worry about when state is changed using store.subscribe(), the connect will let you know each state change whenever it gets updates.
Also, react core concept is based on props and states. Using connect allows you to get redux state as props. Using this.props :)
And ah, I remembered at what condition I accessed the store directly rather than using connect. In my project, I needed to save all the redux state in different form to somewhere and I din't need to connect it to any component. In this case, direct usage with redux store is very easy and helpful. But if we try the same with connect in this case, then we'll have a difficult time.
Thus, I would suggest you to use them in separate condition.
Use connect if you want to map with component.
Access redux store directly if you don't need to map with component.
Further, this blog will explain a bit more: react redux connect explained
Redux Flow:
Using connect with react component:
To conclude: Using connect, you use the provider and it lets the every child component to access the store by providing a provider and using store props in root app component.
Total React noob here. I have two sibling Components in my React application. I want to display a Loading message in Component A when a certain method is executed in Component B
I'm familiar with passing state from a parent to child in React, but how can a sibling be notified to display it's loader?
Here's what I have so far
export default class ComponentA extends Component {
constructor(props) {
super(props);
this.state = {};
}
render(){
return(
{/* Loader */}
{ this.state.loading === true &&
<div className="loader"></div>
}
)
}
}
export default class ComponentB extends Component {
// Constructor, bindings and stuff...
getData(){
// Update Component A's "loading" state here.
// Once the data is fetched, set "loading" to false
}
render(){
return(
<div>
<button onClick="this.getData"></button>
<table>
<tbody>
{/* ... */}
</tbody>
</table>
</div>
)
}
}
Setting up both Components to inherit state from a Parent Component doesn't seem desirable because I'll have many siblings with nested Components that I'll also want to trigger this loader
Any input is appreciated!
In React, data flows from the top, down. You'd need to wrap Component A and B in a parent and keep the state in the parent as a single point of truth.
However, as you have said after a while this can become tedious when dealing with components several levels deep. You don't want to have to keep passing the state down.
I'd suggest looking into Redux (the docs are excellent) as well as the container(smart)/component(dumb) architecture.
In addition to the links above, I'd really suggest taking the time to watch this free tutorial series from Redux's creator, Dan Abramov.
Hope that helps!
First of all, you said that Component A is a child component of Component B but I don't see any relationship between the two components. But you can do one of 2 things in my opinion. One is to import Component A into Component B as a child component and pass the data to Component A as props (easy). Second way requires you to understand redux. In this case, I don't see the necessity due to the size of the app. However, basically you can have a mastermind at the top of the React component tree to pass down the state to any component. So updating the store by dispatching the new data from Component B will enable Component A to have an access to the data. Redux would be great from large applications and for data that needs to be distributed in multiple places.
I've used higher order component to share a function between my components.With this implementation,the function comes as a prop in my component.The app supports multi languages so in each component a key is passed and the hash value is obtained to display. Hash values are passed to all the components using the context. Now getSkinHash access the context and returns the hash value.
const {getSkinHash} = this.props; //shared function,accesses the context
const value = getSkinHash(SOME_VALUE);
No problem with this implementation but getting the function out of prop every time leads to writing lot's of boilerplate code in all the components.
Is there a better/alternate ways to achieve this?
Thanks
React works with properties, so you can't just say you don't want to work with properties. That is when sharing data between components.
As far as you can do to shorten
const {getSkinHash} = this.props;
const value = getSkinHash(SOME_VALUE);
is to:
this.props.getSkinHash(SOME_VALUE).
If that is a generic function, not component dependent, you can choose to import it into your component just like you import other stuff.
import { myFunction } from './functions'
Then you would simple call it with myFunction.
If you need your function to synchronize data between your components, use a Redux action and connect your components to the global state. Your other components will get to know value hash changes too.