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

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>
)
}

Related

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 pass props from parent to grandchild component in react

I have tried pass value from parent to grandchild component, and it works. While I am thinking if there is another simpler or other way of passing props in shorter path.
What I did is quite cumbersome in codesandbox
There may be a common problem in react world called prop drilling by passing data to children only using props.
I would recommend only 2-level passing, if you need pass data deeper then you probably doing something wrong.
Use one of popular state management library (if your project is big) or React context (which is awesome)
Create a folder called /contexts and put contexts there. The structure of files can be like shown below:
First you need to create a context itself
type ClientContextState = {
data: User;
set: (data: User) => void;
logout: () => void;
};
// empty object as a default value
export const UserContext = createContext<UserContextState>({} as UserContextState);
Then create a Provider wrapper component
export const UserProvider = ({ children }: Props) => {
const [data, setData] = useState<User>({});
const sharedState = {
data,
set: setData
logout: () => setData(null)
}
return <UserContext.Provider value={sharedState}>{children}</UserContext.Provider>
});
You may also want to have an alias of useContext hook:
export const useUser = () => {
return useContext(UserContext);
};
After all this whenever you wrap your components or app to <UserProvider>...</UserProvider> you can use our hook to access data and methods form sharedState from any place you want:
export LogoutButton = () => {
const {data, logout} = useUser();
return <Button onClick={() => logout()}>Logout from {data.name}</Button>
}
Whenever you want to pass props or data from Grandparent to child component, always use react-redux. This is useful to maintain the state and access the data from anywhere/any component.
Another way is to use useContext hooks which you can use to pass the props
Following are the steps to use useContext hooks
Creating the context
The built-in factory function createContext(default) creates a context instance:
import { createContext } from 'react';
const Context = createContext('Default Value');
The factory function accepts one optional argument: the default value.
Providing the context
Context.Provider component available on the context instance is used to provide the context to its child components, no matter how deep they are.
To set the value of context use the value prop available on the
<Context.Provider value={value} />:
function Main() {
const value = 'My Context Value';
return (
<Context.Provider value={value}>
<MyComponent />
</Context.Provider>
);
}
Again, what’s important here is that all the components that’d like later to consume the context have to be wrapped inside the provider component.
If you want to change the context value, simply update the value prop.
Consuming the context: Consuming the context can be performed in 2 ways.
The first way, the one I recommend, is to use the useContext(Context) React hook:
import { useContext } from 'react';
function MyComponent() {
const value = useContext(Context);
return <span>{value}</span>;
}
Generally it's helpful to consider whether moving state down the hierarchy would be the simplest route. That means lifting the component instantiation to a place closer to the state being used. In your example, that could mean Component_data is used inside Component and passed to its children there, removing one step in the nested data flow. Even better, would be that Child.a accesses Component_data.A directly.
In a real app with cases where accessing the data directly is less feasible, a solution I lean towards is using Context to set data in the parent that retrieves it, and then I can access it however deeply nested the component might be that needs it.
i.e. in App I would create the Context provider, and in ChildA I access it via useContext hook.
Further reading
https://reactjs.org/docs/context.html
https://overreacted.io/before-you-memo/#solution-1-move-state-down (this post is about an alternative to using useMemo but has an illustrative example of why moving state down is a good thing)

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

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

GraphQL HOC messes with ref to children | React

I am using React and Apollo for my project.
I got this in my component MenuModal:
onClick = () => {
console.log(this.child)
this.child.onSubmit(); // do stuff
};
render() {
return (
<Modal.Content scrolling>
<MenuEdit
ref={ref => (this.child = ref)} // Using ref to access it's properties
selectedValues={selectedValues}
match={match}
menu={menu}
/>
My component MenuEdit has a function defined in class:
onSubmit = values => {
console.log('calling inner form submit', values);
if (this.child) {
this.child.submitFromOutside();
}
};
I should be able to call onSubmit from MenuModal right?
But I am currently getting this:
And when I console.log this.child in my onClick function I can see this:
So there's no onSubmit function there. When seeing GraphQL I wondered if it had something to do with me exporting the component with the graphQL HOC.
export default compose(
graphql(UPDATE_MENU, { name: 'updateMenu' }),
withApollo,
withRouter
)(MenuEdit);
And when I changed it just to:
export default MenuEdit;
I can see my function
So I wonder how I write my export so I still can access my function in my child. Thanks.
The HOC wrapps your component into another component. You can see this in the React devtools. You will see that the component renders a component around your wrapped component. Something like this
<Apollo(ChildComponent)>
<ChildComponent />
</Apollo>
Your ref then points to the Apollo(ChildComponen) element instance.
What you are doing here looks like an antipattern to me. In React we usually don't call functions on rendered elements (except sometimes DOM elements). The idea is rather that children call functions of their parents by receiving them as properties. The best thing in your case is to get rid of the reference and move your state and actions up the component chain. Alternatively you can use the new render prop style in react-apollo.
There was a contribution to the Apollo repository to address this issue...
https://github.com/apollographql/react-apollo/pull/410
Wrapping your component export like this withApollo(Component, { withRef: true }) will expose your child methods. Accessible using ref.current.wrappedInstance.

How can I reset a react component including all transitively reachable state?

I occasionally have react components that are conceptually stateful which I want to reset. The ideal behavior would be equivalent to removing the old component and readding a new, pristine component.
React provides a method setState which allows setting the components own explicit state, but that excludes implicit state such as browser focus and form state, and it also excludes the state of its children. Catching all that indirect state can be a tricky task, and I'd prefer to solve it rigorously and completely rather that playing whack-a-mole with every new bit of surprising state.
Is there an API or pattern to do this?
Edit: I made a trivial example demonstrating the this.replaceState(this.getInitialState()) approach and contrasting it with the this.setState(this.getInitialState()) approach: jsfiddle - replaceState is more robust.
To ensure that the implicit browser state you mention and state of children is reset, you can add a key attribute to the root-level component returned by render; when it changes, that component will be thrown away and created from scratch.
render: function() {
// ...
return <div key={uniqueId}>
{children}
</div>;
}
There's no shortcut to reset the individual component's local state.
Adding a key attribute to the element that you need to reinitialize, will reload it every time the props or state associate to the element change.
key={new Date().getTime()}
Here is an example:
render() {
const items = (this.props.resources) || [];
const totalNumberOfItems = (this.props.resources.noOfItems) || 0;
return (
<div className="items-container">
<PaginationContainer
key={new Date().getTime()}
totalNumberOfItems={totalNumberOfItems}
items={items}
onPageChange={this.onPageChange}
/>
</div>
);
}
You should actually avoid replaceState and use setState instead.
The docs say that replaceState "may be removed entirely in a future version of React." I think it will most definitely be removed because replaceState doesn't really jive with the philosophy of React. It facilitates making a React component begin to feel kinda swiss knife-y.
This grates against the natural growth of a React component of becoming smaller, and more purpose-made.
In React, if you have to err on generalization or specialization: aim for specialization. As a corollary, the state tree for your component should have a certain parsimony (it's fine to tastefully break this rule if you're scaffolding out a brand-spanking new product though).
Anyway this is how you do it. Similar to Ben's (accepted) answer above, but like this:
this.setState(this.getInitialState());
Also (like Ben also said) in order to reset the "browser state" you need to remove that DOM node. Harness the power of the vdom and use a new key prop for that component. The new render will replace that component wholesale.
Reference: https://facebook.github.io/react/docs/component-api.html#replacestate
The approach where you add a key property to the element and control its value from the parent works correctly. Here is an example of how you use a component to reset itself.
The key is controlled in the parent element, but the function that updates the key is passed as a prop to the main element. That way, the button that resets a form can reside in the form component itself.
const InnerForm = (props) => {
const { resetForm } = props;
const [value, setValue] = useState('initialValue');
return (
<>
Value: {value}
<button onClick={() => { setValue('newValue'); }}>
Change Value
</button>
<button onClick={resetForm}>
Reset Form
</button>
</>
);
};
export const App = (props) => {
const [resetHeuristicKey, setResetHeuristicKey] = useState(false);
const resetForm = () => setResetHeuristicKey(!resetHeuristicKey);
return (
<>
<h1>Form</h1>
<InnerForm key={resetHeuristicKey} resetForm={resetForm} />
</>
);
};
Example code (reset the MyFormComponent and it's state after submitted successfully):
function render() {
const [formkey, setFormkey] = useState( Date.now() )
return <>
<MyFormComponent key={formkey} handleSubmitted={()=>{
setFormkey( Date.now() )
}}/>
</>
}
Maybe you can use the method reset() of the form:
import { useRef } from 'react';
interface Props {
data: string;
}
function Demo(props: Props) {
const formRef = useRef<HTMLFormElement | null>(null);
function resetHandler() {
formRef.current?.reset();
}
return(
<form ref={formRef}>
<input defaultValue={props.data}/>
<button onClick={resetHandler}>reset</button>
</form>
);
}

Resources