I'm new in react hooks and I just don't see this on docs:
const MyComponent = ({myProp}) => {
const [myPropHook, setPropHook] = useState(myProp)
...
}
I'm wondering if this is a good practice?
The value you pass to useState is used as a starting value for the state variable. So, when your component props change, they will not affect the state variable you are using. The initial value would be the first props sent to the component and after that can be modified only using the setPropHook function.
So, in short, it is definitely a code smell to use props as initializers for useState because reading the code does not correctly convey what will actually happen.
You don't see it much because it doesn't make a lot of sense in terms of how a React app should distribute its state.
If a prop value is set higher up the tree, it shouldn't be used as part of the separate state within a component. It makes sense to use prop values to determine the state of a component indirectly as in 'if the prop is this, then set the state to that', but not to directly copy the prop in to the initial value.
In other words, the internal state of a component (accessed via the useState and useReducer Hooks) should be determined by the component, not directly by the parent(s).
Yes, this is bad. What you're doing is passing a prop to the state, and it is discouraged by many.
The React docs says that "using props to generate state often leads to duplication of “source of truth”, i.e. where the real data is.". The danger is that if the props is changed without the component being refreshed, the new prop value will never be displayed, because the initialization of state from props only runs when the component is first created.
The only exception would be to use the prop as a seed for an internally-controlled state. After several years of react development, I've never encountered such a case.
Further reading:
React component initialize state from props (SO question)
React Anti-Patterns: Props in Initial State (medium.com article)
Why Setting Props as State in React.js is Blasphemy (blog post)
If you are trying to receive a prop to that functional component, then yes, but not exactly like you have it written. So in the parent component you will have something like this:
const App = () => {
const [resource, setResource] = useState("posts");
and then there is a component inside the JSX like so:
const App = () => {
const [resource, setResource] = useState("posts");
return (
<div>
<div>
<button onClick={() => setResource("posts")}>Posts</button>
<button onClick={() => setResource("todos")}>Todos</button>
</div>
<ResourceList resource={resource} />
</div>
);
};
That ResourceList component has to be able to receive the props that the App component is passing to it. Inside a class-based component you would do {this.props.resource}, but in our case, where its a functional component using React hooks you want to write it like so:
const ResourceList = (props) => {
const [resources, setResources] = useState([]);
or via ES6 destructuring like so:
const ResourceList = ({ resource }) => {
const [resources, setResources] = useState([]);
Related
I'm relatively new to React and JavaScript and I'm building a website but I'm having a bit of an issue with passing Data via components from child to parent.
So:
I have my App.js script which is used as a router with react-router-dom, but I would like to store a boolean value using the useState hook. This boolean value that I would like stored should be passed on from a component called Login. I have the script setup to pass that data however the boolean is only stored as long as the Login COmponent page is active and when it is not rendered the boolean store by the useState hook in the App.js script just goes to 'undefined'. I'm assuming that this is happening because the app.js page constantly re-loads and re-renders, so how could I store that value even when the login page is not being rendered?
This is the code setup to pass that data:
app.js
const [authValue, setAuthValue] = useState(false);
const changeValue = (value) => {
setAuthValue(value)
}
And where the Login is called:
<Route path='/signin' element={<Login changeValue={changeValue}value={authValue} />} />
Login.jsx:
const Login = ({changeValue, value}) => {
const [isValid, setIsValid] = useState(true)
changeValue(isValid)
}
The useState hook in React is a solution for component-level state management in functional components.
So, the value of isValid is stored only in Login.js and can be passed as a props to its children components.
Of course, you can also pass some state (or values) from child to parent via functions passed from parent into the child, but this is not the way you should consider if you want to use a state in whole app.
If you need to have the state that should be persistent across the app components, even after one component was unmounted, you should consider global management solutions like React Context API, Redux, MobX or similar libraries.
Try putting the changeValue(isValid) into a useEffect() hook. So:
useEffect(() => {
changeValue(isValid)
}
To explain the situation. I have a child component that has one chevron that spins on a click. I need to implement from child to parent and to grand parent props data.
<ChildComponent>
<ParentComponent>
<GrandParentComponent>
Inside ChildComponent I have arrow icon ( img )
const [rotateIcon, setRotateIcon] = useState(false);
const expandAttachment = useCallback(() => {
setRotateIcon(!rotateIcon);
}, [rotateIcon]);
<img src="assets/arrow" transform={rotateIcon ? 'rotate(90deg)' : 'rotate(0)'} >
And this is work but.... I need to props rotateIcon state to GrandParentComponent compoentent.
It’s not just the child to the parent. Than child to parent and from parent another level up to. GrandParent.
How do I do that? Is this a good way at all? What are my alternatives? I do not use redux in the system, but api context!
To be clear. All three components are connected.
Each to each is a child parent!
As I see it you have 3 options:
Store the state props in the parent of all components, which is too complex for this purpose I think.
Use Redux to handle state management for the app
Use Context API to handle the state management for the app as a global state.
You can check out this blog for this:
https://www.loginradius.com/blog/engineering/react-context-api/#:~:text=What%20is%20Context%20API%3F,to%20parent%2C%20and%20so%20on.
I do however use and recommend using redux for complex operations like this. When you understand the idea behind redux it becomes very easy to use it.
store the state in the GrandParentComponent and pass through in between components as a prop to reach ChildComponent.
Props are meant to be passed from a parent component to a child component(s).
You can do prop drilling i.e, adding a state to <GrandParentComponent/> component and passing it as prop to the child components.
grandparent
export default GrandParentComponent(){
const [rotateIcon, setRotateIcon] = useState(false);
// pass the state and setState down to the children
return <ParentComponent rotateIcon={rotateIcon} setRotateIcon={setRotateIcon}/>
}
parent
export default ParentComponent({rotateIcon, setRotateIcon}){
// pass props to child component again
return <ChildComponent rotateIcon={rotateIcon} setRotateIcon={setRotateIcon}/>
}
child
export default ChildComponent({rotateIcon, setRotateIcon}){
const expandAttachment = useCallback(() => {
setRotateIcon(!rotateIcon);
}, [rotateIcon]);
<img src="assets/arrow" transform={rotateIcon ? 'rotate(90deg)' : 'rotate(0)'} >
}
There’s nothing inherently bad about prop drilling, but it gets harder to manage the state of the app as the complexity increases.
To avoid this we either use React Context or Redux. React Context is baked into react itself, so it's very lightweight and powerful at the same time. It is the perfect fit for a situation like yours.
To keep the answer short, read on how to implement React Context and useContext hook here.
I am trying to build an ecommerce website, and I hit a problem I cannot seem to resolve. I am very new to react and JS so have some patience please :)
I declared 4 useStates in my app.js:
const [elementeDinState, setElementeDinState] = useState([]);
const [currentCategorie, setCurrentCategorie] = useState("Acasa");
const [subCategorie, setSubcategorie] = useState([]);
const [cartContents, setCartContents] = useState([]);
const fetchData = useCallback(async () => {
const data = await getCategories();
setElementeDinState(data);
}, []);
useEffect(() => {
fetchData().catch(console.error);
}, [fetchData]);
const changeHeader = (dataFromMenuItem) => {
setCurrentCategorie(dataFromMenuItem);
};
const changeCopiiContent = (data1FromThere) => {
setSubcategorie(data1FromThere);
};
const changeCart = (dataFromCart) => {
setCartContents(dataFromCart);
};
I am passing the functions to change those states to different child components as props. my problem is, when I add items to cart it triggers a re render of my component (products listing component) that should not be affected by cartContents and that resets the state of said component to the initial value that changes the items being shown. does useState hook create a single global state comprised of all those states?
If these useState are defined in the app.js and then passed down, when a child will use them chasing the state will happen in the app.js so all the children of <App /> will be re-rendered.
I guess that your app.js looks similar:
function App() {
const [elementeDinState, setElementeDinState] = useState([]);
// ...and the other hooks and methods
return (
<cartContents setElementDinState={setElementeDinState} />
<ProductList />
)
}
In this case the state is in the component so when <CartContents /> changes it, it will trigger a re-render of the and all its children <ProductList /> included.
To avoid this problem think better when each piece of state needs to be and put the state as near as possibile to that component. For example, if the state of the cart does not influence the Product list. Move the useState in the <Cart /> component.
From what I understand, your problem is that you're simply resetting the cartContents state every time you call the changeCart function, correct?
What you probably want, is to add (or remove ?) the item to the cart, like this?
const changeCart = (dataFromCart) => {
setCartContents(oldContents => [...oldContents, dataFromCart]);
};
Here is a description of useState from the oficial site:
useState is a Hook (...). We call it inside a function component to add some local state to it
So it creates just a local state.
About your problem, We need more information, but I believe that some parent component of that widget is trying to render other component instead of your the component that you wanted (let's call it "ProblemComponent") and rendering you ProblemComponent from scratch again, before you can see it.
it's something like that:
function ParentComponent(props: any) {
const isLoading = useState(false);
// Some logic...
if(isLoading) {
return <LoadingComponent/>;
}
return <ProblemComponent/>;
}
If that doesn't work you can also try to use React.memo() to prevent the ProblemComponent to update when it props change.
well, seems like I wanted to change the way react works so I figured out a work around, based on what you guys told me. I declared the state of the productsComponent in the parent component and adding to cart now doesn't force a refresh of the items being shown. thank you!
I have the following component where within the useEffect, I am calling some data reading related
functions meant to happen once on load.
The problem is, some of the prop data are not available at this stage (still undefined) like the prodData and index.
They are only available when I get into the Nested components like <NestedComponent1 />.
I wish to move this logic into the nested components which will resolve this issue.
But I do not want to repeat these code inside the useEffect for each component. Instead looking to write these 7 lines once maybe in a function
and just call it with the 3 NestedComponents.
Issue is that there is a higher order function wrapping here plus all the values like prodData and index is coming from Redux store.
I can't just move all these logic inside useEffect into a normal JS function and instead need a functional component for this.
And if I make a functional component to perform these operations, I can't call it in the useEffect for each of the NestedComponents.
Cos this is not valid syntax.
React.useEffect(() => {
<NewlyCreatedComponentWithReadingFunctionality />
}, []);
Thus my query is, is there a way I could write a functional component which has the data reading logic inside its useEffect.
And then extend this functional component for each of the functional components so that the useEffect would just fire
when each of these NestedComponents are called?
Doesn't seem to be possible to do this thus looking for alternatives.
This is the existing component where some of these prop values are undefined at this stage.
const MyComponent = ({
prodData,
index,
country,
highOrder: {
AHigherOrderComponent,
},
}) => {
// this is the logic which I am looking to write once and be
// repeatable for all the NestedComponent{1,2,3}s below.
React.useEffect(() => {
const [, code] = country.split('-');
const sampleData = prodData[index].sampleData = sampleData;
const period = prodData[index].period = period;
const indication = prodData[index].indication = indication;
AHigherOrderComponent(someReadDataFunction(code, sampleData));
AHigherOrderComponent(someReadDataFunction(code, period);
AHigherOrderComponent(someReadDataFunction(code, indication);
}, []);
return (
{/* other logics not relevant */}
<div>
<div>
<NestedComponent1 />
<NestedComponent2 />
<NestedComponent3 />
</div>
</div>
);
};
export default connect( // redux connect
({
country,
prodData,
index,
}) => ({
country,
prodData,
index,
})
)(withHighOrder(MyComponent));
React components implement a pattern called composition. There are a few ways to share state between parts of your React application but whenever you have to remember some global state and offer some shared functionality, I would try and manage that logic inside a context provider.
I would try the following:
Wrap all your mentioned components inside a context provider component
Offer the someReadDataFunction as a callback function as part of the context
Within your provider, manage react state, e.g. functionHasBeenCalled that remembers if someReadDataFunction has been called already
Set functionHasBeenCalled to true inside someReadDataFunction
Call someReadDataFunction inside your components within a useEffect based on the props data
This way, your application globally remembers if the function has been executed already but you can still use the latest data within your useEffect within your components to call someReadDataFunction.
With React classes when you have state in the constructor you could inherit it to the child component directly and from the child to the parent using callbacks. How can you transfer state from parent to child with hooks? Is the only way useReducer or Redux?
The concepts of passing props down to child or conveying information from child to parent hasn't changed with the arrival of hooks.
Hooks provide, you a way to use lifecycle like functionality and states with functional components.
you can declare your state in parent with useState and pass it down as props to child component as you would normally have done with class components or functional components previously
For example:
const Parent =() => {
const [count, setCount] = useState(0);
return <Child count={count} setCount={setCount} />
}
const Child = ({count, setCount}) => {
const updateCount = () => {
setCount(prev=> prev + 1);
}
return (
<div>
<div>Count: {count}</div>
<button type="button" onClick={updateCount}>Increment</button>
</div>
}
You can refer this post for more details on lifecycles with hooks:
ReactJS lifecycle method inside a function Component
Please refer the react docs with hooks FAQs
Classes and functional components (or func-comp as my mate calls them) are the same in respect to props.
You can pass props from parent to child in a functional component just like how you'd do with a class.
//Parent
const Parent = () => {
const [state, setState] = React.useState({ products: 1, isAvailable: true})
const addProduct = (data) => {
// Your function
}
return (
<Child product info={state} addProduct={addProduct} />
)
}
export default Parent
And in the child component you can receive the props typically the way you would will classes.
const Child = ({productInfo, addProduct}) => {
// Do what ever you like with the props
}
Cheers!
Perhaps, you should ask yourself why you would like to use inheritance. It seems like for many cases where many developers tend to immediately think about using OOP-style inheritance, React.js might recommend composition instead (see https://reactjs.org/docs/composition-vs-inheritance.html).
With functional components, composition is probably the only choice, which means that your "parent" component would render the "child" component, passing whatever state it needs to pass via the child's props.
Whether your project needs Redux or not should be completely orthogonal to the composition-vs-inheritance question.