Note: I'm using React Native but this applies to Reactjs also. I only use stateless functional components by design. I'm wanting to learn and use functional programming as much as possible. Does this essentially mean I can never use connect() from react-redux? Should I be adding more container components to my app design?
Here is an example of my components:
export const Height = (props, { store }) => {
const state = store.getState()
return (
<View style={formStyles.container}>
<DimenInput
value={state.get('height').get('height1').toString()}
onChangeText={text => store.dispatch(updateHeight(text, 1))}
/>
<Text style={formStyles.text}>{'&'}</Text>
<DimenInput
value={state.get('height').get('height2').toString()}
onChangeText={text => store.dispatch(updateHeight(text, 2))}
/>
</View>
)
}
Height.contextTypes = {
store: React.PropTypes.object
}
I also want to learn all common redux and react techniques that I will see in the industry so I wanted to learn mapStateToProps() and connect(). I started to try and create these functions for my component above:
const mapStateToProps = (state) => {
return state.get('height').get('height1').toString()
}
const mapDispatchToProps = (dispatch) => {
return {
onHeightChanged: (text) => {dispatch(updateHeight(text, 1))}
}
}
Before realising that I cannot use those functions on this stateless component. I have to use them on a container component that would essentially hold this component but I don't really have one, the container of this component is:
export const Volcalc = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>HEIGHT</Text>
<Height/>
<Text style={styles.text}>WIDTH</Text>
<Width/>
</View>
)
}
And then the container of that is just:
const App = () => {
return (
<Provider store={store}>
<Volcalc/>
</Provider>
)
}
The app I'm making is a single form - so it is tiny. At this stage I cannot see any situation where I would HAVE to use a container component. In a larger app would I be forced to use container components and then I will get the chance to use connect()? Which technique is better out of using only stateless, and using a container created with connect(), if it could be done either way?
Edit: this egghead lesson does something in between where they have a stateless component AddTodo that uses dispatch from the store and they call connect() on it. Possibly the scenario which would fit my use case for Height although I will need to keep passing it the full store since it uses store.get and store.dispatch - unless I pass it the store value for height instead of the full store.
By using connect on a container component you are keeping your app functional. Your data flows down to children. Your state is immutable.
Container components are useful in regards to connect because you're isolating your call to the store and, again, letting data flow down to child components. It's just a good way to structure your app and reduces the number of points the store is called in your app. It makes development much easier to manage and presents fewer opportunities to create a mess.
You don't have to use connect(), but your components will then become more tightly coupled with redux. If you have Height container and you can remove those redux store code from Height component and make it reusable easily on other non-redux project.
Using class as object-oriented pattern and some functional oriented patterns all mixed together makes a beautiful result. It's perfect if you want to practice your functional programming skills, but sadly, if you do so for a more serious application, you will loose a lot of benefits from react and redux. Things like state, your application will be hard to update. Your code will be coupled. and close to modifications. In a bigger application, you will find it frustrating developing it. I took the exact same path not long ago and I'm enjoying react and redux way more mixing both. This is my opinion. If you want to use connect, component state and most of the advantages of react and redux with keeping it fully functional, you can use React.createClass(), this is a function receiving an object with other function in it. Like instead of initializing the component state in the class constructor, you will use a function, getInitialState in this case. Containers are very useful to get reusable components. Another problem is passing redux props down to some components. With a small app, it's easy to pass those props not more than 3 level deep, but in a big app, it's not. Imagine, each time your redux state change, your whole app re-render and to avoid losing your data, you will have store those data in your redux state. You redux store will be too big and will contain useless data and data that shouldn't be there.
To sum up, that's perfectly fine to do a small app in full functional mode, but for a bigger app, you will loose to much react and redux benefits, making your app hard to modify and develop.
It is good to see some other peoples thoughts on this because I'm coming in new. But i gather from the other answers on this question that there are some performance optimisations that connect() takes care of and also it is good to not pass the whole store to a component because the react advanced context feature is unstable and also it couples the component to redux when using the redux store in the component.
I have gone over the tutorials some more and realised I can use connect() on my stateless component giving the benefits above.
Code:
const mapStateToProps = (state) => {
return {
height1: state.get('height').get('height1').toString(),
height2: state.get('height').get('height2').toString()
}
}
const mapDispatchToProps = (dispatch) => {
return {
updateHeight: (text, number) => {
dispatch(updateHeight(text, number))
}
}
}
let Height = (props) => {
return (
<View style={formStyles.container}>
<DimenInput
value={props.height1}
onChangeText={text => props.updateHeight(text, 1)}
/>
<Text style={formStyles.text}>{'&'}</Text>
<DimenInput
value={props.height2}
onChangeText={text => props.updateHeight(text, 2)}
/>
</View>
)
}
Height.propTypes = {
height1: React.PropTypes.string.isRequired,
height2: React.PropTypes.string.isRequired,
updateHeight: React.PropTypes.func.isRequired
}
Height = connect(
mapStateToProps,
mapDispatchToProps
)(Height)
Related
for example my component tree structure likes the picture,i want to implement that when i press component B's button (or touchable..), i want to change component A's color or shape.
this is terrible when i use only props and useState.
you can recommend other external libraries.
i have been using redux but not working because when i touch B, A is not re-rendered.
can you suggest a way or hooks or libraries?
You might use any state management library out there (such as Redux or MobX), but in a relatively simple case (in which your state isn't that complex), you can use React.Context whose purpose is precisely to avoid Prop Drilling and allow unrelated trees to share information (as long as this information is provided by a common ancestor).
I'm working on a complex react app too with a similar component tree.
I think the Redux approach is relevant.
Touching B should update the redux state
Then a useEffect hook placed in component A should take the Redux state as dependency in order to trigger the effect.
Something like that :
(the redux part can differ depending on the version)
const CompB = () => {
function sayHelloToCompA() {
useDispatch(updateReduxState('Hello compA')) // triggers redux action that will update redux state
}
return (
<TouchableOpacity
style={styles.button}
onPress={() => sayHelloToCompA()>
<Text>Press me</Text>
</TouchableOpacity>
)
}
Once Redux state is updated, then :
const CompA = () => {
const { messageFromCompB } = useSelector(state => state.yourReduxSlice)
useEffect(() => {
console.log(messageFromCompB)
}, [messageFromCompB]);
return (
...
)
}
Sorry for answer my question. it is just my mistake of redux so state is not changed.
I don't keep vaild data format.
I fix my data appropriately, it is worked.
This is a part of Think in React.
Thinking in React is the hard part for me because I see many developers do React with different mindsets.
When I was writing code for the Notification component that will be used by developers, suddenly I've noticed that there are different experiences to use the component:
Passing many Props like Bootstrap
<Notification
title="New Feature"
body={message}
action={action}/>
Passing one Prop as an Object
const data = {
title:"",
subtitle:"",
message:""
}
<Notification data={data}/>
Passing nested Children
<Notification>
<Title></Title>
<Body><Body/>
<Action><Action>
</Notification>
I followed the passing nested Children because ( I guess) It seems if I scale the component, I don't need to provide a Bootstrap-like experience for the developers.
import React from "react"
import { Wrapper, Text } from "./Styled"
const Body = ({ message }) => (
<Wrapper>
<Text>{message}</Text>
</Wrapper>
)
export default Body
The problem is I'm thinking about it is when I want to scale the Component and let's say adding 3 additional features that require 3 additional props
I'm confused about the reasons why each approach might be chosen, and what's the "best" developer experience.
To answer this question let's review all possibilities given React Element and a Function Component:
const c1 = <div>React Element</div>;
const C2 = () => <div>Function Component</div>;
Notice that from performance perspective, every component can be memoized and not cause useless renders.
Pass React element through props
const ObjectProps = ({ component }) => {
console.log("render object props");
return component;
};
<ObjectProps component={c1} />
Pros
Simple.
Lets you define the exact usage of passed component (contract).
For example you might decide "messages" have to be on top of "actions"
Cons
No lazy rendering
Passing heavy component may cause performance issues.
Hard to inject props (need to use React.cloneElement).
As a library writer you would like to inject your styles and refs.
Passing Function Component
const FunctionComponentProps = ({ FunctionComponent }) => {
console.log("render function component");
return <FunctionComponent />;
};
<FunctionComponentProps FunctionComponent={C2} />
Pros
Lazy rendering.
Easy to pass props and inject props for inner implementation.
Lets you define the exact usage of passed component (contract).
Cons
Confusing
Children Render
const ChildrenRender = ({ children }) => {
console.log("render function component");
return children;
};
<ChildrenRender>
{c1}
<C2 />
</ChildrenRender>
Pros
No restriction for the developer (no contract)
Cons
Hard to inject children (need to use React.Children API + React.cloneElement combo)
No contract
The developer might pass "buttons" and then "messages" and break the view.
Implementing ChildrenRender usually comes with component instances which results a minimal "contract" mentioned above.
const ChildrenRender = ({ children }) => {...};
ChildrenRender.InnerComp1 = <SomeComponent .../>
ChildrenRender.InnerComp2 = <SomeComponent2 .../>
<ChildrenRender>
<ChildrenRender.InnerComp1>{c1}</ChildrenRender.InnerComp1>
<ChildrenRender.InnerComp2><C2/></ChildrenRender.InnerComp2>
</ChildrenRender>
In Conclusion
It heavily depends on the component's usage, usually the hybrid approach suits well - passing components through props and add an option for passing children too.
Another technique is Render Props.
TL;DR: I'm wondering if a solution I've thought of will cause performance issues.
The title is a bit confusing so let me clarify. A standard way of defining my header button component would be something like this (the code is using typescript but it's not relevant to the problem so you can probably ignore it):
import React from "react";
const HeaderButton = (props: Client.Common.Header.HeaderButton.Import) => {
const service = useHeaderButton();
return <div>
//header component JSX here
<div>
};
export default HeaderButton;
I want to change it up a bit. I've found I would much rather be able to expose some internal component methods to the parent component. In this case, I would like to be able to provide a toggle method to the parent instead of using an "active" property to determine the active state of the button. My reasoning is that I'd rather avoid having to set up the toggle logic in every parent component I use my HeaderButton in if I can instead define it in my button component and then have the parents use that method.
I've done this and it works as I'd like it to (so far, at least). I'm relatively new to both React and programming in general and self taught so I have gaps in my knowledge. I'm not that knowledgeable about React under the hood and how it does its performance optimizations etc so I'm worried I've done something that will cause unpredictable issues. Here's what I've done:
//Header.tsx (this component is using the regular style)
import React, { useEffect } from "react";
import "./Header.scss";
import HeaderButton from "./Components/HeaderButton/HeaderButton";
const Header = () => {
const HeaderButtonService = HeaderButton({ renderProps: <div>TEST</div>, class: "languageSelectionButton" });
useEffect(() => {
setTimeout(() => {
HeaderButtonService.toggle();
}, 5 * 1000);
}, []);
return (
<div id="headerBar">
<div className="headerNavigationButtonsContainer"></div>
{HeaderButtonService.view}
</div>
);
};
export default Header;
//HeaderButton.ts
import HeaderButtonView from "./HeaderButtonView";
const HeaderButton = (props: Client.Common.Header.HeaderButton.Import) => {
const service = useHeaderButton();
return {
toggle: service.toggleActive,
view: HeaderButtonView({ ...props, service.active })
};
};
export default HeaderButton;
//HeaderButtonView.tsx
import React from "react";
import "./HeaderButton.scss";
const HeaderButtonView = (props: Client.Common.Header.HeaderButton.Import) => {
return (
<div className={"headerButton" + (props.active ? " active" : "")
+ (props.class ? " " + props.class : "")}
style={{ ...props.style }}>
{props.renderProps && props.renderProps}
</div>
);
};
export default HeaderButtonView;
In my solution, I import HeaderButton.ts instead of HeaderButton.tsx. The parent component passes the relevant props to HeaderButton.ts which passes them down to HeaderButtonView.tsx while adding the "active" prop which it gets from a custom hook, not the parent component. It then returns the result of invoking HeaderButtonView with these new props as well as the method to toggle the active state.
This is a simple example but I would potentially use this template to expose state values and multiple methods to parent components.
The code works, it renders what I want it to render and toggles the active state after 5 seconds.
My concern is that, not knowing all that much about how react works under the hood, I might have created an optimization problem. Is there any reason I shouldn't be using this pattern?
I have done some testing and it doesn't seem to break anything. I added a counter to the state of header.tsx and increment it every 3 seconds then watch for re-renders. I was concerned that react would not be able to recognize that the old and new HeaderButton components are the same but it did. Though react goes through the component tree, it doesn't re-render the button (except after the first 5 seconds when activity is toggled).
Also, should HeaderButton.ts be a hook? It's working as intended atm so I'm not sure what exactly I gain/lose from adding "use" in front of it.
Your approach here is 1) contrary to how 99% of people use React, 2) contrary to very way React is intended to be used, and 3) overcomplicated in a way that adds a level of abstraction to React that absolutely does not need to be there.
1) This is just not how people write React code. Sure it might work for you and make sense on some level but no one else follows this pattern. What about when you start importing and using other people's components? What about when you have to partner with someone else to write an app? What about when you hand off or are handed off a bunch of code that is patterned in a completely different way? There is absolutely a lot to be said for following prevailing (or at least common) coding patterns because it makes your code a lot more interoperable and easy to understand compared to the rest of the framework ecosystem, and vice-versa.
2) React at its very core is intended to be declarative. It is the number one adjective most people would use to describe it, and features heavily on the very front page of the React website. Your proposed pattern here is very un-declarative, and directly defeats not just the declarative nature of React but the inherent patterns of component state and props. I could link you to examples in the documentation as to how declarative coding, state management, and props feature heavily in React design patterns but the list would include practically every page in the website: Components and Props, State and Lifecycle, Lifting State Up, Thinking in React, etc etc.
3) Your proposed pattern is just... needlessly complicated and abstract. It adds a layer of confusion that does not actually make things easier. I can barely follow even your basic minimal example code!
Your core rationale seems to be this:
My reasoning is that I'd rather avoid having to set up the toggle logic in every parent component I use my HeaderButton in if I can instead define it in my button component and then have the parents use that method.
That's a great instinct - make things reusable and modular so that you don't have to repeat yourself too often. Well, you can do that beautifully in React while adhering to the tenants of React's declarative nature!
First let's rewrite your components in a way that is a more "traditional" React style - just make the <HeaderButton> a regular component that accepts an active prop, and storing that state in the parent, <Header>. This is called "lifting state up" and is a key concept in React - state should live at the lowest common denominator that allows the necessary components access to it. In this case the parent <Header> needs access to the state, because it needs to not only pass it into <HeaderButton> as a prop, but be able to modify that state:
const HeaderButton = ({active}) => {
return <div>{active ? 'Active' : 'Inactive'}</div>
};
const Header = () => {
const [active, setActive] = React.useState(false);
const toggleActive = () => {
setTimeout(() => {
setActive(oldActiveState => !oldActiveState);
}, 5 * 1000);
};
return (
<header>
<button onClick={toggleActive} >Toggle active state</button>
<HeaderButton active={active} />
</header>
);
}
Cool, now state lives in the parent, in can modify that state, and it passes that state as a prop to <HeaderButton>. It is very declarative, easy to understand, and it's clear where state lives and what component is rendering what.
Now on to your concern about reusing the toggle logic. What if we want to use <HeaderButton> somewhere else and have the same toggle logic? What if we want to have five header buttons inside of <Header>? Do we need to copy and paste the same logic many times?
React provides a great solution here with custom hooks. Custom hooks allow you to encapsulate logic and state in a clean way. And - this is very important - the state it encapsulates still lives inside of the component that calls the custom hook. This means we can encapsulate the state and logic but they will still "live" inside of <Header> so we have access to it to pass as a prop. Let's try it:
const useHeaderButtonState = () => {
const [active, setActive] = React.useState(false);
const toggleActive = () => {
setTimeout(() => {
setActive(oldActiveState => !oldActiveState);
}, 5 * 1000);
};
return [active, toggleActive];
}
const HeaderButton = ({active}) => {
return <div>{active ? 'Active' : 'Inactive'}</div>
};
const Header = () => {
const [active, toggleActive] = useHeaderButtonState();
return (
<header>
<button onClick={toggleActive} >Toggle active state</button>
<HeaderButton active={active} />
</header>
);
}
Now, the state and the toggle logic live inside of useHeaderButtonState(). When called, it returns both a value (active) and a function for updating that value (toggleActive). Inside of <Header>, we can deconstruct the result of the custom hook call and use it to render.
We could even extend this custom hook even further to return not just the state and updater function, but a component to render. Then, if we want to render multiple instances of a component, including all its associated state and logic, and still have access to the state and logic in the parent component (<Header>), we can do that:
const useHeaderButtonState = () => {
const [active, setActive] = React.useState(false);
const toggleActive = () => {
setTimeout(() => {
setActive(oldActiveState => !oldActiveState);
}, 5 * 1000);
};
const headerButtonComponent = (
<>
<button onClick={toggleActive}>Toggle active state</button>
<HeaderButton active={active} />
</>
);
return [headerButtonComponent, active, toggleActive];
};
const HeaderButton = ({ active }) => {
return <div>{active ? "Active" : "Inactive"}</div>;
};
const Header = () => {
const [
headerButtonComponent1,
active1,
toggleActive1
] = useHeaderButtonState();
const [
headerButtonComponent2,
active2,
toggleActive2
] = useHeaderButtonState();
const [
headerButtonComponent3,
active3,
toggleActive3
] = useHeaderButtonState();
return (
<header>
{headerButtonComponent1}
{headerButtonComponent2}
{headerButtonComponent3}
</header>
);
};
https://codesandbox.io/s/pensive-sutherland-2onx9
Now we're cooking with gas! We're reusing state and logic but doing it in a declarative way that makes sense inside of React.
Sorry to be so heavy handed but I really, really want to discourage you from using the imperative pattern you proposed above. I've been writing React code for 3+ years and trust me when I say that sticking to the established patterns of React will pay off in the long run. Not only is it legitimately easier to write and comprehend, if will help your career to write code that other devs can more easily understand and work with.
I conduct a lot of hiring interviews and if I saw someone submit the code you wrote above, I would think they have no idea how React works or is intended to work and would immediately disqualify them. If you find it difficult or counterintuitive to understand, I would suggest keep on learning and practicing (with a more declarative React-compatible style) until it clicks. Otherwise, perhaps React just isn't the framework for you and you'd be best served with a different framework that more closely matches your preferences, style, and mental models!
Good luck!
Edit: Oh and one last thing I'll touch upon. You mentioned concerns about performance. In this case the performance differences are actually completely negligible and not worth even considering. In general React is very well optimized on its own and you don't need to worry about performance except in very specific edge cases. You should typically only optimize if and when you actually run into a performance bottleneck, and you solve for that. As they say, premature optimization is the root of all evil.
My response instead addresses the core programming pattern that you are proposing here on the basis that it makes the process of developing, debugging, and understanding the code itself needlessly difficult.
I started my app without any state management dependencies and my app's state looked something like this:
state = {
...initState,
}
///some actions I need to perform
handleChange = (e) => {
this.setState(setDefaultValues(e));
this.setState(setBmr);
this.setState(setTdee);
this.setState(setTrainingAndRestDaysCal);
this.setState(setTrainingMacros);
this.setState(setRestMacros);
}
here I import my initState from separate file (to save some space). Then I have handleChange where I'm passing functions to multiple this.setState because my next state data depends on previous. I'm importing those functions from separate file (to save some space as well)
Then I came to the point where I realized I'm passing props all over the place so I introduced the new react's context API. Which works very well in my opinion. Kind of like a redux just without a big boilerplate. So this context API helped me with that prop drilling through the child components. To use the context API i had to do some changes to my state object, so it looks like this now:
state = {
...initState,
handleChange: (e) => {
this.setState(setDefaultValues(e));
this.setState(setBmr);
this.setState(setTdee);
this.setState(setTrainingAndRestDaysCal);
this.setState(setTrainingMacros);
this.setState(setRestMacros);
},
setTrainingTrueOrFalse: (isIt) => {
this.setState({ trainingToday: !isIt })
},
saveNewMeal: (meal) => {
const meals = this.state.meals
this.setState({
meals: { ...meals, meal }
})
}
Here I added my handleChange to my state so I can pass it via context api. Then I have created some new functions on the state an realized my state now is getting too messy.
I have a function on the state (handleChange ) that uses other functions imported from setStateFunctions.js
I have functions where logic is not extracted to setStateFunctions.js
On a high level my app has 2 main components: Calculator & MealPlanner
Calculator - calculates the macros and calories and passes the result
to MealPlanner
MealPlanner - uses data from calculator and creates meals
==================================================
What's the better approach to structure my app's state and functions?
Do I really need to introduce redux?
What would be my reducers structure?
How would this look like using just react's new context API?
Your app is sized right to go without adding redux for state management.
1) What's the better approach to structure my app's state and functions?
Let a top-level app maintain state that includes "handler" functions.
2) Do I really need to introduce redux?
Not needed. Keep your props organized and let the App handle the "events".
3) What would be my reducers structure?
Avoid it and go vanilla react.
4) How would this look like using just react's new context API?
Context feels overkill and possibly entangling (two components could drift on the understanding of how to use what is exposed as shared, global state in the context).
Let your composing App manage the state and pass props to the child components (Calculator and MealPlanner). You want two-way communication between those, so also pass "handling" functions that change the state within App to get the effect to ripple to the other via passed-in props. Something like the following:
class App extends React.Component {
state = {
...initState, // not sure if this is global or what, but assuming defined
handleCalculation: () => {
// do some state changes on this ala this.setState()
},
handlePlanning: () => {
},
};
render() {
return (
<div>
<MealPlanner {...this.state} />
<Calculator {...this.state} />
</div>
);
}
}
Let MealPlanner and Calculator set required propTypes accordingly.
I'm new to React, and I made the mistake of thinking Redux is essential to writing any react app. I wrote all my components to be stateless, but I'm not really seeing the benefit:
It just feels really awkward when I have nested components catering to nested objects. At every component level, I have to pass in the all the previous indexes so the child component can update the correct value. Also, I can't even use the data passed into the FlatList because it won't be Redux aware; I have to use the data from the store.
Is this how stateless components are supposed to be written or am I doing something horribly wrong?
Example:
State structure
{
appetizers: [...],
desserts: [
...array of deserts,
{ fruits: [
... array of fruits,
{ name: 'apple',
calories: 100,
}]
}
]
}
Child class
class Child extends Component {
update() {
const props = this.props;
props.dispatch(actions.eat(props.food, props.dessertIndex, props.fruitIndex));
}
render(){
const props = this.props;
return (
<TouchableHighlight onPress={() => this.update()}>
<Text>{props.desserts[props.dessertIndex].fruits[props.fruitIndex]}</Text>
</TouchableHighlight>
)
}
}
const mapStateToProps = state => ({ desserts: state.desserts })
export default connect(mapStateToProps)(Child);
parent class
class Parent extends Component {
render(){
return (
<FlatList
...props
data={this.desserts[this.props.dessertIndex].fruits}
renderItem={({item, index}) => (<Child food={item}, dessertIndex={this.parent}, fruitIndex={index}>)} />
)
}
}
const mapStateToProps = state => ({ desserts: state.desserts })
export default connect(mapStateToProps)(Parent);
grandparent class
class GrandParent extends Components {
render() {
return (
{ this.desserts.map((item, index) => {
return <Child data={item} dessertIndex={index} />
}) }
)
}
}
const mapStateToProps = state => ({ desserts: state.desserts })
export default connect(mapStateToProps)(GrandParent)
A few thoughts:
First, Redux is not essential to building a React app. However, there are very good reasons to use Redux with React - I talked about a few of them in a post on why using Redux and React together is a good idea.
Second, I've seen a number of people say that you "should keep absolutely all your state in Redux, and only use functional components". I completely disagree with that. Per the Redux FAQ entry on using Redux state vs React component state, it's up to you as a developer to decide what state lives where. In addition, per this Dan Abramov tweet, it's totally fine to use class components if you need state or lifecycle methods.
Third, it looks like your current Redux state structure is storing very nested data. You can do that, but it's not the recommended approach. Instead, you are encouraged to keep your state flattened and normalized as much as possible, which simplifies lookups like the ones you're dealing with. See the Redux docs section on Structuring Reducers - Normalizing State Shape for examples.
Fourth, the best pattern for good Redux performance and simpler props management is to have many components connected to the Redux store, especially for things like list items. You can pass in item IDs and other values as props to the connected component, and use those values in its mapState function to extract only the item that one specific component instance needs. I've got examples of this in my post Practical Redux, Part 6: Connected Lists, Forms, and Performance, and more articles on the topic in the Redux Performance section of my React/Redux links list.
Hopefully that points you more in the right direction. If you've got more questions, please come drop by the Reactiflux chat channels on Discord and ask! The invite link is at https://www.reactiflux.com .