I want to do something like this:
const GreetingWithCounter = (props) => {
const { name, count } = props;
return (
<div>
<div>Hello {name}</div>
<button onClick={() => render({ ...props, count: count + 1 })}>
{count}
</button>
</div>
);
}
<GreetingWithCounter name="Alice" count={0} />
Ie. I want to re-render a component with new values for its props. Is there a way to do that? Looking through these three questions, I'm seeing ways to re-render a component but not with new values for props (1, 2, 3).
Context
I'm thinking about a way to simplify React. I really like the mental model of React being the view layer in MVC, where UI = F(state). But things can get confusing when "state" can come from so many different places: props, useState, useReducer, "raw" useContext, Redux (which uses useContext I think), whatever else.
What if everything was just based off of props?
For local state you'd do what I did in that example above. You'd initialize the local state of count when doing <GreetingWithCounter name="Alice" count={0} /> and then update it by re-rendering. This means less DRYness because you'd have to repeat the count={0} code instead of only having it once inside of GreetingWithCounter.
You'd have to do prop drilling instead of useContext stuff.
This approach would probably make React slower.
Still, I hypothesize 1) that the mental model of having everything coming from props is simpler and 2) that pro outweighs the cons in a non-trivial amount of apps.
Props are not supposed to be mutated in React. That is precisely the difference between props and state. The React way to do this is to use state for the count. You can pass the initial state of the count as a prop and do this: const [count, setCount] = useState(initialCount). Your onClick handler would then increment count, which again is state. I realize that this is not what you want but it's how React works.
In React Props values cannot be changed in child component but we can do it in parent component.
const GreetingWithCounter = (props) => {
const { name, count, updateCount } = props;
return (
<div>
<div>Hello {name}</div>
<button onClick={updateCount}>{count}</button>
</div>
);
};
function App() {
const [count, setCount] = useState(0);
const updateCount = () => {
setCount(count + 1);
};
return (
<div className='App'>
<h1>Greeting With Counter:</h1>
<GreetingWithCounter
name='Alice'
count={count}
updateCount={updateCount}
/>
</div>
);
}
Appreciate the change you want to point out and value you want to add but there might be some points that you're missing what React conceptually trying to provide with seperation between props and state.
The props within components coming with React are specifically conceptually designed to be immutable as per the documentation
here.
So what you're trying to do is conceptually not ok for that purpose and violating what React tries to accomplish.
Infact you may mention about creating another library/framework which successfully getting it done while introducing props are the new state concept but in this specific case, there's no possible way to succeed on it in a React way.
You cannot change value of props in child but you have 2 ways to handle it
first, I assume that you only want to use count in child component and you don't need count value in parent, in this case you can use props.count as initial state, sth like this :
const GreetingWithCounter = props => {
const [count, setCount] = useState(props.count);
const { name } = props;
return (
<div>
<div>Hello {name}</div>
<button onClick={() => setCount(prevState => prevState + 1)}>{count}</button>
</div>
);
};
<GreetingWithCounter name="Alice" count={0} />;
but if you wanna access it's value from parent, it's better to pass setter to child
sth like this :
const GreetingWithCounter = ({name,count,setCount}) => {
return (
<div>
<div>Hello {name}</div>
<button onClick={() => setCount(prevState => prevState + 1)}>{count}</button>
</div>
);
};
const App = ()=>{
const [count, setCount] = useState(0);
return (<GreetingWithCounter name="Alice" count={count} setCount={setCount} />)
}
or if it's child is so deep that you need to send props to all it's tree, its better to use state management like Redux,Context or ...
Is this the way you want to do ? :
import React from 'react'
import ReactDOM from 'react-dom'
export default function renderComponent(Component, props, container) {
ReactDOM.render(<Component {...props} />, container)
}
What you are trying to do goes against the philosophy of state management of react. For correct way to do it, you can check other answers, and even you yourself have posted it in the questions.
But if you really want to do it, behind its magic, React is also just JavaScript. Therefore, we just need to implement the render function outside of React way of thinking. We know that React re-renders on state change magic or on props change. We need to just somehow connect the render method you asked for with set state. Something like the below should work.
const ParentStuff = () => {
const [props, setProps] = useState({ name: "Alice", count: 0 });
render = setProps;
return (<GreetingWithCounter name={props.name} count={props.count} />);
}
let render;
const GreetingWithCounter = props => {
const { name, count } = props;
return (
<div>
<div>Hello {name}</div>
<button onClick={() => render({ ...props, count: count + 1 })}>{count}</button>
</div>
);
};
A lot of people will scream though at code above. It definitely strays away from the intended use.
If you want to go further, you can also just have one state for the entire app, and pass this state fo every component. And voila! You just created a singleton state and an uni directional data flow, which is a poor man version of the redux and this will probably kill performance of the webapp, as things like typing each letter in a textbox will re-render the entire page.
As others already mentioned, component is either controlled or uncontrolled (or mix of both) in react.
If you keep state in component itself - it's uncontrolled. You can reset its state to internal by changing key prop from parent though.
If you keep state in parent - it's controlled component and changes it's state through props/callbacks.
What you have shown in your example, you want to achieve uncontrolled component with some syntactic sugar on top.
Example implementation:
const usePropsWithRender = (props) => {
const [currentProps, setCurrentProps] = useState(props);
return {
...currentProps,
render: setCurrentProps,
};
};
const GreetingWithCounter = (props) => {
const { name, count, render } = usePropsWithRender(props);
return (
<div>
<div>Hello {name}</div>
<button onClick={() => render({ ...props, count: count + 1 })}>
{count}
</button>
</div>
);
};
You can reuse usePropsWithRender through all you project, but it's nothing more than a thin wrapper around useState. I don't see how it is better than using useState directly.
I have a problem making a HOC component with hooks inside.
I am trying to find a correct solution to not break rules-of-hooks.
If I change the name of this component to start with lowercase: withNetworkDetector
instead of uppercase, I will not get errors, but did I break the rule then?
Is this code will be secure then?
export const WithNetworkDetector = (Component: FC<{}>) => (props: any) => {
const [isDisconnected, setIsDisconnected] = useState(false);
const handleConnectionChange = useCallback(() => {
...
setIsDisconnected(true);
}, []);
return (
<div>
{isDisconnected && (
<Toast type="warning" text={TOAST_ERRORS.LOST_INTERNET_CONNECTION} />
)}
<Component />
</div>
);
};
Hooks can be used inside the React functional component. One of the criterias that classify a function as a react function is the name starting in uppercase, then only we can use that as a React component. So, it will break the rule.
Imagine that I have a function which dynamically generates some of a component's props, and I want to pass them all at once without being explicit about every prop the function could generate. Normally you can do this with the spread operator, but the issue with the spread operator is that it created a new object each time. This would mean that (if I understand correctly) during the React Reconciliation, the component would have new props every time and would rerender every time, even if the props the function generated are the same.
Here's a concrete example:
const generateProps = () => ({foo: 'bar'});
const ParentComponent = () => ({
const someProps = generateProps();
return (
<SomeComponent><ChildComponent {...someProps} otherProp='hello world'/></SomeComponent>
)
})
Here ChildComponent would render every time ParentComponent would render (right?). One thing I know you could do is wrap the ChildComponent with a React.memo and do a deeper comparison of the props (passing a custom comparison function to it), but what if you don't have control over ChildComponent? Are you forced into being explicit? Or am I incorrect and ChildComponent wouldn't rerender in this example (assuming ChildComponent simply consumes the props and doesn't use any contexts or anything).
Thank you!
You have it wrong. Reconciliation doesn't look at the props. It mainly looks at the component type, e.g.
if on one render you render
<Comp1/>
and on next render, on the same place in the component tree, you render:
<Comp2/>
it will unmount Comp1 and mount Comp2 because the type of components is different. If component types are the same, it will update existing one. There are some more details but you can check them yourself.
Furthermore, the props are also compared in a shallow way by default if you use React.memo, so if on one render you pass
let y = {a:1,b:2};
....
<Comp1 {...y}/>
and on next render you pass
let x = {a:1,b:2};
...
<Comp1 {...x}/>
Default comparison of React.memo will assume that props didn't change, because a and b have same values.
You can verify here, clicking on the div doesn't re render the Test component:
let Test = React.memo(props => {
console.log(props);
return <div>{props.a}</div>;
});
function App() {
let [state, setState] = React.useState({ a: 123 });
return (
<div
onClick={() => {
setState({ a: 123 });
}}
>
<h1>Hello StackBlitz!</h1>
<Test {...state} />
<p>Start editing to see some magic happen :)</p>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
In my normal React class Components, I have done some checks in the render() method, before returning conditional html rendering. Now, I was using a react functional component, which apparently does not have the render() method... how would I do the conditional checks here? Just Inside normal functions and then return html code from those functions?
e.g Class Component:
render() {
let test;
if (this.state.test ===true) {
test = (
<p>This is a test</p>
)
}
return(
{test}
)
}
in functional components? :
return (
<p >
{checkIcon()} //normal Javascript functions?
</p>
)
As stated by others you can do anything inside a render function, the same things you could do with a class component. You can think of your functional components as the render function of your class ones...
Functional components, by the way, should not contain that much business logic, it'd be better to enhance them with HOCs and function composition.
You might want to have a look at recompose, in which my example takes inspiration from. (change the test attribute and press run code snippet)
// First create a Generic HOC that embedds the branching logic for you.
const branch = (predicate, LeftComponent) => RightComponent => props => (
predicate(props) ? <LeftComponent {...props} /> : <RightComponent {...props} />
);
// Leave your view component the only job of displaying data to the screen. Avoid any control flow.
const Test = () => 'this is a test component';
const Value = ({ value }) => <div>The Value is {value}</div>;
// Create a final component that branches accordingly with the needed check (if props.test is true)
const Component = branch(
props => props.test,
Test
)(Value);
ReactDOM.render(
<Component test={true} value="£100" />,
document.getElementById('container')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="container"></div>
You can think of functional component as a render method of class component where you can do the exact same thing that you do in render except that you will receive props from the arguments instead of this and similarly you won't have state unless your using hooks. So you would pass test as a prop to the functional component
const MyComponent = ({test}) =>{
let value;
if (test ===true) {
test = (
<p>This is a test</p>
)
}
return(
{value}
)
}
I'm learning a udemy course and in it we create this which is a function that takes a component and a class name as the arguments and returns a wrapped JSX having the WrappedComponent nested inside a <div>.
This is going to be real easy but I don't understand the syntax for props => (). Why do we use the props just after return statement? I understand that inside ( ) is the JSX to return. Maybe someone can easily explain why the props is there and how it gets handled?
import React from 'react';
const withClass = (WrappedComponent,className) => {
return props => (
<div className={className}>
<WrappedComponent/>
</div>
);
};
export default withClass;
The example what you copied is a common react pattern called HOC (Higher Order Component). What happens here is the following we have a function which takes a component as an argument ( WrappedComponent ) and we are returning a definition of a new component which will wrap our WrappedComponent. You could wrote the following as well
const withClass = (WrappedComponent,className) => {
return class extends React.Component {
render() {
return(
<div className={className}>
<WrappedComponent/>
</div>
)
}
}
};
So basically the syntax props => () is just a way to define a new component. It is worth to mention that the syntax itself is used to declare an arrow function.