Can you pass down state to child components with React Hooks? - reactjs

I'm trying to re-write some of my code in React Hooks, and I was wondering, when I pass down searchData in this example... Will the now have access to searchData and setSearchData? I'm confused about how passing around state in React Hooks works.
import React, { useState, useEffect } from "react";
import SearchBarContainer from "../SearchBar/SearchBarContainer";
import PostContainer from "./PostContainer";
function PostPage(props) {
const [searchData, setSearchData] = useState([...props.data]);
const [addLike, setAddLike] = useState([]);
useEffect(() => {
setAddLike({ addLike: Array(props.data.length).fill(false) });
});
return (
<div>
<SearchBarContainer data={props.data} searchData={searchData} />
<div css={parentPostContainer}>
{searchData.map((dataOnMap, index) => {
return (
<PostContainer
data={dataOnMap}
addLike={addLike[index]}
searchData={searchData}
/>
);
})}
</div>
</div>
);
}
export default PostPage;

Yes, as far as passing down props are concerned there is no difference in usage with React Hooks. In the snippet you've provided just pass setSearchData as a prop to PostContainer.
<PostContainer
data={dataOnMap}
addLike={addLike[index]}
searchData={searchData}
setSearchData={setSearchData}
/>

Yes Absolutely you can. In fact React docs talk about "Lifting state up" so to speak.
If there is shared state between components and you need to lift it up to their nearest ancestor.
The general concept is props are passed down and state is passed up.
However, this can get messy. If you are working on a larger project, I would recommend using Redux.
You can pass the state as props, but if you want child components to alter state, then you can pass the setter from the useState directly to child components.
Here is the same example:
<PostContainer
data={dataOnMap}
addLike={addLike[index]}
searchData={searchData}
setSearchData={setSearchData}
setAddLike={setAddLike}
/>
Or another solution
const LikeManager = {
addLike: setAddLike,
data: {
//some data
}
}
<PostContainer
data={dataOnMap}
likeManager: {LikeManager}
/>
I know this is not part of the question, but I would recommend using scalar values for useState wherever possible

Related

Is it possible to pass data to components without a parent?

I understand that the normal way is to pass props through a parent, but I want to know how else I can pass props to a component.
let C1 = createReactClass({
...
render: function() {
console.log('render C1');
return (
<div>
<button onClick={this.cambiaAAzul}>Azul</button>
<button onClick={this.cambiaAVerde}>Verde</button>
<button onClick={this.cambiaARojo}>Rojo</button>
<p>Estado C2 <strong style={ {color: this.state.color} }>{this.state.color}</strong></p>
<C2 color={this.state.color}/> // It is common
</div>
);
}
});
Typically, to solve the props-drilling problem, which is what seems to be the issue you are trying to solve, the useContext hook should help.
So set up a context,
const C1ContextProvider = ({children}) => {
//...
// the Provider gives access to the context to its children
return (
< C1Context.Provider value={someValue}>
{children}
</C1Context.Provider >
);
}
... and then in your component where you need someValue, you can just do this:
import React, { useContext } from "react";
import { C1Context } from "../C1Context";
//...
const values = useContext(AirDCPPSocketContext);
cons foo = values.someValue; // <-- someValue obtained from the context
//..
I don't know how it is possible to not have parents but you can pass informations by local storage and context hooks.
Props are by definition arguments passed into a React component. Since React components are instantiated in the context of their parent, I'm not sure if it would be possible to pass props in any other way.
There are many other ways to get data in React though. You could query data from an API or other source, or you could pull from a state manager (such as redux). It just depends on what you are trying to accomplish.
Props are simply things passed into a component when it's called.
For instance:
C1.js
function C1((prop1, prop2) {
<h1>{prop1} {prop2}</h1>
})
App.js
<C1 prop1="Hello" prop2="World" />
<C1 prop1="React's" prop2="Great" />
By referencing the C1 component in App.js, you can call it with different props to produce different results.
Now I'm not too familiar with the createReactClass and render: you're using, as this is a style of React I've never learned. However, I'd guess that props provide the same function.
Your question asks about props without a parent. Well. Props without a parent wouldn't be props, they'd just be normal JS data types inside your file.
Hope this helps :)
The component's local state only can drill into child components. for passing props without using React component's local state. you have to use state management libraries like Context API or Redux
These libraries are created to make you able to pass props into nonrelated or sibling components
Example with redux:
Here I Assume that you set up redux in your project. imagine you have two components that you want to pass props without parent-child relation.
const changeMyWeightAction = (weight) => {
return {
type: 'CHANGE_MY_WEIGHT',
payload: weight
}
}
const JenniferFunctionComponent = () => {
const dispatch = useDispatch()
return (
<div>
<span>hello I'm Jennifer and I can decide my weight</span>
<input onChange={e => dispatch(changeMyWeightAction(e.target.value)})/>
</div>
)
}
const WeightFunctionComponent = () => {
const weight = useSelector(state => state.Jenny.weight)
return (
<span>Jenny's weight is {weight}</span>
)
}

What is the issue when consuming an state in child component after useEffect?

I have the following situation that I can't explain.
What I'm trying to do, is to use an state variable from a parent component in a child component.
When I load some new values into this state using a 'useEffect' call, only the Second component renders correctly. In this component I use 'props' variable to access the values.
The First component only renders what I initially set in the 'useState' call of the parent, ignoring the new state set in the useEffect call. In this component I set its state initially using the same props property.
Why is this occurring?
Here is the code and a snippet of the render.
App.js
import './App.css';
import First from './First';
import Second from './Second';
import { useEffect, useState } from 'react';
function App() {
const [someVar, setSomeVar] = useState({dos:['a']});
useEffect(()=>{
console.log('useEffect')
setSomeVar({dos:['a', 'b', 'c']})
}, [])
return (
<div className="App">
<header className="App-header">
<First letters={someVar}></First>
<hr/>
<Second letters={someVar}></Second>
</header>
</div>
);
}
export default App;
First.js
import { useState } from "react";
export default function First(props) {
const [letters, setLetter] = useState(props.letters.dos);
return (
<div>
{letters.map((letter) => {
return <span key={letter}>{letter}</span>;
})}
</div>
);
}
Second.js
export default function Second(props) {
return (
<div>
{props.letters.dos.map((letter) => {
return <span key={letter}>{letter}</span>;
})}
</div>
);
}
Result
The value passed to useState is only used by React when a component is first mounted to determine the initial state value. On every future render, the state value used by that component will be taken from that component's own prior state, or from a prior call to that component's state setter.
At the moment that you do
const [letters, setLetter] = useState(props.letters.dos);
you're splitting up your app's state into two completely separate parts; one with someVar in the parent component, and another with letters in the First child component. Further changes to either one of those states will not affect the other.
To avoid this sort of problem, and to keep your data organized, it's usually a good idea to not duplicate state in more than one place. If you want a child component to be able to access and manipulate state linked to the parent, have the parent pass down the state, and if needed, the state setter, without putting another useState in the child.

Composition In React with params

I am trying to create an architecture that in some way imitates the slots from VUE.
The idea is for the parent component to be able to inject some props into the component and the child can inject the rest of the props.
This is how I tried to approach this problem, unfortunately this approach will not work because the compontent will be "monut" every time the parent re-render takes place.
Filters = (prams) => {
useEffect(()=>{ //RENDER ALL THE TIME },[])
...
}
ParentComponent = () => <ChildComponent Filters={(props) => <Filters propA={"A"} />}
ChildComponent = (props) => {
const Filters = props.Filters;
render(<Filters probB="B" />)
}
I know, I can use useCallback for ((props) => <Filters propA={"A"} />), but only it will help only if what I want to pass to "propA" is steady.
I want to "manage" <Filters /> component in parent, so that the child does not have to handle Filters logic (props).
React gives you proper API to do most things. Using it forces you into certain paradigms that are proven to work well.
You should probably have a look at the Context and Memo APIs from React.
Or if you have to select and update state from multiple components, you might wanna have a look at libraries that provide global state, like Redux and Recoil.
Context example
// The shape
interface ContextProps {
myProp: string
}
// The context
export const MyContext = React.createContext<Partial<ContextProps>>({
myProp: 'nothing'
});
// The provider
<MyContext.Provider value={{ myProp: 'override' }}>
{children}
</MyContext.Provider>
// Consumer
const { myProp } = useContext(MyContext)
In some case you can also use useMemo or React.memo and use your own custom compare function if needed to prevent re-renders in very specific situations.

How to use Refs in Function based Component [duplicate]

I was going through the hooks documentation when I stumbled upon useRef.
Looking at their example…
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
…it seems like useRef can be replaced with createRef.
function TextInputWithFocusButton() {
const inputRef = createRef(); // what's the diff?
const onButtonClick = () => {
// `current` points to the mounted text input element
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
Why do I need a hook for refs? Why does useRef exist?
The difference is that createRef will always create a new ref. In a class-based component, you would typically put the ref in an instance property during construction (e.g. this.input = createRef()). You don't have this option in a function component. useRef takes care of returning the same ref each time as on the initial rendering.
Here's an example app demonstrating the difference in the behavior of these two functions:
import React, { useRef, createRef, useState } from "react";
import ReactDOM from "react-dom";
function App() {
const [renderIndex, setRenderIndex] = useState(1);
const refFromUseRef = useRef();
const refFromCreateRef = createRef();
if (!refFromUseRef.current) {
refFromUseRef.current = renderIndex;
}
if (!refFromCreateRef.current) {
refFromCreateRef.current = renderIndex;
}
return (
<div className="App">
Current render index: {renderIndex}
<br />
First render index remembered within refFromUseRef.current:
{refFromUseRef.current}
<br />
First render index unsuccessfully remembered within
refFromCreateRef.current:
{refFromCreateRef.current}
<br />
<button onClick={() => setRenderIndex(prev => prev + 1)}>
Cause re-render
</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
tldr
A ref is a plain JS object { current: <some value> }.
React.createRef() is a factory returning a ref { current: null } - no magic involved.
useRef(initValue) also returns a ref { current: initValue } akin to React.createRef(). Besides, it memoizes this ref to be persistent across multiple renders in a function component.
It is sufficient to use React.createRef in class components, as the ref object is assigned to an instance variable, hence accessible throughout the component and its lifecyle:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)
useRef(null) basically is equivalent to useState(React.createRef())[0] 1.
1 Replace useRef with useState + createRef
Following tweet has been enlightening for me:
useRef() is basically useState({current: initialValue })[0].
With insights from the tldr section, we now can further conclude:
useRef(null) is basically useState(React.createRef())[0].
Above code "abuses" useState to persist the returned ref from React.createRef(). [0] just selects the value part of useState - [1] would be the setter.
useState causes a re-render in contrast to useRef. More formally, React compares the old and new object reference for useState, when a new value is set via its setter method. If we mutate the state of useState directly (opposed to setter invocation), its behavior more or less becomes equivalent to useRef, as no re-render is triggered anymore:
// Example of mutating object contained in useState directly
const [ref] = useState({ current: null })
ref.current = 42; // doesn't cause re-render
Note: Don't do this! Use the optimized useRef API instead of reinventing the wheel. Above is for illustration purposes.
createRef always returns a new ref, which you'd generally store as a field on a class component's instance. useRef returns the same ref upon every render of a functional component's instance. This is what allows the state of the ref to persist between renders, despite you not explictly storing it anywhere.
In your second example, the ref would be re-created upon every render.
Just to highlight a purpose:
createRef is as simple as return {current: null}. It's a way to handle ref= prop in most modern way and that's it(while string-based is toooo way magic and callback-based looks too verboose).
useRef keeps some data before renders and changing it does not cause re-render(as useState does). They are rarely related. Everything you expect for class-based component go to instance fields(this.* =) looks like candidate to be implemented with useRef in functional components.
Say useCallback works as bounded class methods(this.handleClick = .....bind(this)) and may be re-implemented(but we should not re-invent the wheel for sure) with useRef.
Another examples are DOM refs, timeout/interval IDs, any 3rd party libraries' identifiers or references.
PS I believe React team better chose different naming for useRef to avoid confusion with createRef. Maybe useAndKeep or even usePermanent.
A ref is a plain JS object { current: }.
React.useRef(initValue) return a ref { current: initValue }
it is remember ref value across multiple render of function component.
It is advise to use in Function component
React.createRef(initValue) also return a ref { current: initValue }
it is not remember ref value across multiple render of function components. It is advise to use in class based component
Yet another but important addition to other's answers.
You can't set a new value for createRef. But you can for useRef.
const ur = useRef();
const cr = createRef();
ur.current = 10; // you can do it, and value is set
cr.current = 10; // you can, but it's no good, it will not change it

How to bind one React Functional Component to another? (DevExtreme Chart to PivotGrid)

We prefer to use functional statetless react components as much as possible (especially now with React Hooks).
However, I cannot figure out how to achieve the effect of binding a DevExtreme PivotGrid to a Chart (so that one gets a combined operation).
As you can see from their example: https://js.devexpress.com/Demos/WidgetsGallery/Demo/PivotGrid/ChartIntegration/React/MaterialTealDark/
They use Class components with ref's and then do when the combined Class component mounts:
componentDidMount() {
this._pivotGrid.bindChart(this._chart, {
dataFieldsDisplayMode: 'splitPanes',
alternateDataFields: false
});
}
We would rather not go that way. Is there some neat trick one can use with the new React useRef() hook, or by giving HTML doc id's to the chart so that we can bind the two components at some time after the initial load?
We are not purists here, so even slightly ugly solutions would do.
I can't think of anything but a simple change to doing it the Hooks way, aka put the code in the useEffect() hook and use useRef() to bind the refs:
// import useEffect and useRef hook from 'react',
import React, { useEffect, useRef() } from 'react'
function App() {
// initialize the ref variables with null as the initial value,
const pivotGrid = useRef(null);
const chart = useRef(null);
// useEffect runs after the layout paint...
useEffect(() => {
// do the binding on the references,
pivotGrid.current.bindChart(chart.current, {
dataFieldsDisplayMode: 'splitPanes',
alternateDataFields: false
});
}
,
// we pass an empty array to only run once after the component mount,
[])
// pass the refs variables to the components.
return (
<React.Fragment>
<Chart ref={chart}>
<Size height={320} />
<Tooltip enabled={true} customizeTooltip={customizeTooltip} />
<CommonSeriesSettings type={'bar'} />
<AdaptiveLayout width={450} />
</Chart>
<PivotGrid
id={'pivotgrid'}
dataSource={dataSource}
allowSortingBySummary={true}
allowFiltering={true}
showBorders={true}
showColumnTotals={false}
showColumnGrandTotals={false}
showRowTotals={false}
showRowGrandTotals={false}
ref={pivotGrid}
>
<FieldChooser enabled={true} height={400} />
</PivotGrid>
</React.Fragment>
);
}
I'm not 100% sure if you need to pass the ref.current or simply the ref itself . You can try both ways!
EDIT
Re: using useRef(), from the React docs:
Keep in mind that useRef doesn’t notify you when its content changes.
Mutating the .current property doesn’t cause a re-render. If you want
to run some code when React attaches or detaches a ref to a DOM node,
you may want to use a callback ref instead.

Resources