React Native change state of dummy data in modal - javascript

can anyone help me how to do to be able change a stat of 'reserved' from true to false in dummy data in home.js from modalDetails.js ?
https://github.com/FikVlk/Vozickov

This works I believe. I added a button to log your state before and after the change so you can verify that it works. Feel free to remove it, of course.
import React, { useState } from 'react';
import { StyleSheet, Text, View, FlatList, Button } from 'react-native';
import ItemList from './itemList';
import Card from '../components/card';
import { globalStyles } from '../Styles/globalStyles';
export default function home({ route, navigation }) {
const [list, setList] = useState([
{
title: 'Kishal',
body: 'asdasdd',
reserved: true,
id: '1',
},
{
title: 'Kishal',
body: 'asdasdd',
reserved: false,
id: '2',
},
]);
if (route.params) {
for (let i = 0; i < list.length; i++) {
if (list[i][id] === route.params[id]) {
if (list[i].reserved !== route.params.reserved) {
setList((prevList) => {
const copy = [...prevList];
copy[i].reserved = route.params.reserved;
return copy;
});
}
}
}
}
const reserveCar = () => {
console.log('yes');
};
return (
<View styles={globalStyles.container}>
<FlatList
data={list}
renderItem={({ item }) => (
<Card>
<ItemList item={item} navigation={navigation} />
</Card>
)}
/>
<Button title="LOG STUFF" onPress={() => console.log(list)} />
</View>
);
}
const styles = StyleSheet.create({});
The easier and better way is to put the state you have in the home component into redux. This is logical because both the modal and your home component need access to the list and need to know which ones are reserved. It also seems likely that more components will need this information in the future as it seems to be one of the main functions of the app you're building.
You can find documentation about react-redux here: https://react-redux.js.org/using-react-redux/connect-mapstate. It's a rather complicated thing to learn, but exceptionally useful after you've learned it.
Stephen Grider has a great video that's the best explanation I've seen. Link: https://www.youtube.com/watch?v=3sjMRS1gJys

Related

Refactoring Search component from App js to Search js

So im following along with a book "The Road To React" By: Robin Wieruch. The book has you do everything inside the App.js file...which is extremely disappointing because this is obviously terrible practice. Anyway, ive extracted everything else to their own component files and i cant figure out how to extract the search to its own file. Ive attached an img showing the folder structure.
In App.js im trying to extract everything, leaving a return that returns the components like it should be. As it is the App.js file looks like this:
import React, { useState } from 'react';
import './App.css';
import Greeting from '../Greeting/Greeting';
import Search from '../Search/Search';
import List from '../List/List';
const useSemiPersistentState = (key, initialState) => {
const [value, setValue] = React.useState(
localStorage.getItem(key) || initialState
);
React.useEffect(() => {
localStorage.setItem(key, value);
}, [value, key]);
return [value, setValue]
};
// App component
const App = () => {
const stories = [
{
title: 'React',
url: 'https://reactjs.org/',
author: 'Jordan Walke',
num_comments: 3,
points: 4,
objectID: 0,
},
{
title: 'Redux',
url: 'https://redux.js.org/',
author: 'Dan Abramov, Andrew Clark',
num_comments: 2,
points: 5,
objectID: 1,
},
];
// Set state on searchTerm, setSearchTerm with custom hook
const [searchTerm, setSearchTerm] = useSemiPersistentState(
'search',
'React'
);
// Get the value of search input
const handleSearch = (event) => {
setSearchTerm(event.target.value);
};
// Check if user input matches stories array
// toLowerCase() both values
const searchedStories = stories.filter((story) => story.title.toLowerCase().includes(searchTerm.toLowerCase())
);
// Render
return (
<div className="App">
<Greeting name="Colin" age="28" occupation="Front-end developer" />
<InputWithLabel
id="search"
value={searchTerm}
isFocused
onInputChange={handleSearch}
>
<strong>Search:</strong>
</InputWithLabel>
<hr />
<List list={searchedStories} />
</div>
);
}
// Search bar
// Destructure props search, onSearch
const InputWithLabel = ({ id, value, type = "text", onInputChange, isFocused, children, }) => {
const inputRef = React.useRef();
React.useEffect(() => {
if (isFocused && inputRef.current) {
inputRef.current.focus();
}
}, [isFocused]);
return (
<>
<label htmlFor={id}>{children}</label>
<input
ref={inputRef}
id={id}
type={type}
value={value}
autoFocus={isFocused}
onChange={onInputChange}
/>
</>
)
}
export default App;
The List component which is a parent of the Item component looks like so:
import React from 'react';
import Item from '../Item/Item';
function List({ list }) {
return (
<ul>
{list.map(({ objectID, ...item }) => (
<Item key={objectID} {...item} />
))}
</ul>
);
}
export default List;
The Item component looks like so:
import React from 'react';
function Item({ title, url, author, num_comments, points }) {
return (
<div>
<li>
<span>
<a href={url}>{title}</a>
</span>
<span> {author}</span>
<span> {num_comments} comments,</span>
<span> {points} points.</span>
</li>
</div>
);
}
export default Item;
The Greeting Component and Helpers folder are unrelated in any way so i wont post them.
Just as a note all of the code in here works... and at the time im not really interested in refactoring this unless you care to. Im just trying to extract all of that nonsense that relates to what should be a separate Search component out of App.js and into Search.js. Have been trying and ive hit a wall with this as im still new with react.
Here are the errors shown once i move all of the Search content related code from App.js to Search.js and attempt to import to the Search component into App.js
****Failed to compile****
src/Components/App/App.js
Line 60:8: 'InputWithLabel' is not defined react/jsx-no-undef
Line 62:16: 'searchTerm' is not defined no-undef
Line 64:24: 'handleSearch' is not defined no-undef
Line 69:19: 'searchedStories' is not defined no-undef
Search for the keywords to learn more about each error.

React HOC working on some but not other components

I'm using a HOC component to bind an action to many different types of element, including SVG cells, which, when an onClick is bound normally, it works, but when I use my HOC it returns un-intended results.
Minimally reproducible example: https://codesandbox.io/s/ecstatic-keldysh-3viw0
The HOC component:
export const withReport = Component => ({ children, ...props }) => {
console.log(Component); //this only prints for ListItem elements for some reason
const { dispatch } = useContext(DashboardContext);
const handleClick = () => {
console.log('clicked!'); //even this wont work on some.
const { report } = props;
if (typeof report === "undefined") return false;
dispatch({ type: SET_ACTIVE_REPORT, activeReport: report });
dispatch({ type: TOGGLE_REPORT });
};
return (
<Component onClick={handleClick} {...props}>
{children}
</Component>
);
};
Usage working:
const ListItemWIthReport = withReport(ListItem); //list item from react-mui
{items.map((item, key) => (
<ListItemWithReport report={item.report} key={key} button>
{/* listitem children*/}
</ListItemWithReport>
))}
Usage not working:
const BarWithReport = withReport(Bar); //Bar from recharts
{bars.map((bar, index) => (
<BarWithReport
report={bar.report}
key={index}
dataKey={bar.name}
fill={bar.fill}
/>
))}
The ListItem works 100% as anticipated, however, the bars will not render inside of the BarChart. Similarly, with a PieChart the Cells will actually render, with the correct sizes according to their values, however, props like "fill" do not appear to pass down.
Am I using the HOC incorrectly? I don't see an option other than HOC for the inside of Charts as many types of elements will be considered invalid HTML?
You might be dealing with components that have important static properties that need to be hoisted into the wrapped component or need to have ref forwarding implemented in order for their parent components to handle them. Getting these pieces in place is important, especially when wrapping components where you don't know their internals. That Bar component, for example, does have some static properties. Your HOC is making those disappear.
Here's how you can hoist these static members:
import hoistNonReactStatic from 'hoist-non-react-statics';
export const withReport = Component => {
const EnhancedComponent = props => {
const { dispatch } = useContext(DashboardContext);
const handleClick = () => {
const { report } = props;
if (typeof report === "undefined") return false;
dispatch({ type: SET_ACTIVE_REPORT, activeReport: report });
dispatch({ type: TOGGLE_REPORT });
};
return (
<Component onClick={handleClick} {...props}/>
);
};
hoistNonReactStatic(EnhancedComponent, Component);
return EnhancedComponent;
};
Docs on hoisting statics and ref forwarding can be found in this handy guide to HOCs.
There may be some libraries that can take care of all these details for you. One, addhoc, works like this:
import addHOC from 'addhoc';
export const withReport = addHOC(render => {
const { dispatch } = useContext(DashboardContext);
const handleClick = () => {
const { report } = props;
if (typeof report === "undefined") return false;
dispatch({ type: SET_ACTIVE_REPORT, activeReport: report });
dispatch({ type: TOGGLE_REPORT });
};
return render({ onClick: handleClick });
});
Of course, if the parent component is checking child components by type explicitly, then you won't be able to use HOCs at all. In fact, it looks like recharts has that issue. Here you can see the chart is defined in terms of child components which are then searched for explicitly by type.
I think your HOC is invalid, because not every wrapper-Component (e.g. HTML element) is basically clickable. Maybe this snipped can clarify what I am trying to say:
const withReport = Component => (props) => {
const handleClick = () => console.log('whatever')
// Careful - your component might not support onClick by default
return <Component onClick={handleClick} {...props} />
// vs.
return <div onClick={handleClick} style={{backgroundColor: 'green'}}>
<Component {...props} />
{props.children}
</div>
}
// Your import from wherever you want
class SomeClass extends React.Component {
render() {
return <span onClick={this.props.onClick}>{this.props.children}</span>
// vs.
return <span style={{backgroundColor: 'red'}}>
{
// Careful - your imported component might not support children by default
this.props.children
}
</span>
}
}
const ReportedListItem = withReport(SomeClass)
ReactDOM.render(<ReportedListItem>
<h2>child</h2>
</ReportedListItem>, mountNode)
You can have the uppers or the unders (separated by vs.) but not crossed. The HOC using the second return (controlled wrapper-Component) is sure more save.
I've used 4 methods successfully to wrap Recharts components.
First Method
Wrap the component in a HOC and use Object.Assign with some overloads. This breaks some animation and difficult to use an active Dot on lines. Recharts grabs some props from components before rendering them. So if the prop isn't passed into the HOC, then it won't render properly.
...
function LineWrapper({
dataOverload,
data,
children,
strokeWidth,
strokeWidthOverload,
isAnimationActive,
dot,
dotOverload,
activeDot,
activeDotOverload,
...rest
}: PropsWithChildren<Props>) {
const defaultDotStroke = 12;
return (
<Line
aria-label="chart-line"
isAnimationActive={false}
strokeWidth={strokeWidthOverload ?? 2}
data={dataOverload?.chartData ?? data}
dot={dotOverload ?? { strokeWidth: defaultDotStroke }}
activeDot={activeDotOverload ?? { strokeWidth: defaultDotStroke + 2 }}
{...rest}
>
{children}
</Line>
);
}
export default renderChartWrapper(Line, LineWrapper, {
activeDot: <Dot r={14} />,
});
const renderChartWrapper = <P extends BP, BP = {}>(
component: React.ComponentType<BP>,
wrapperFC: React.FC<P>,
defaultProps?: Partial<P>
): React.FC<P> => {
Object.assign(wrapperFC, component);
if (defaultProps) {
wrapperFC.defaultProps = wrapperFC.defaultProps ?? {};
Object.assign(wrapperFC.defaultProps, defaultProps);
}
return wrapperFC;
};
Second Method
Use default props to assign values. Any props passed into the HOC will be overridden.
import { XAxisProps } from 'recharts';
import { createStyles } from '#material-ui/core';
import { themeExtensions } from '../../../assets/theme';
const useStyles = createStyles({
tickStyle: {
...themeExtensions.font.graphAxis,
},
});
type Props = XAxisProps;
// There is no actual implementation of XAxis. Recharts render function grabs the props only.
function XAxisWrapper(props: Props) {
return null;
}
XAxisWrapper.displayName = 'XAxis';
XAxisWrapper.defaultProps = {
allowDecimals: true,
hide: false,
orientation: 'bottom',
width: 0,
height: 30,
mirror: false,
xAxisId: 0,
type: 'category',
domain: [0, 'auto'],
padding: { left: 0, right: 0 },
allowDataOverflow: false,
scale: 'auto',
reversed: false,
allowDuplicatedCategory: false,
tick: { style: useStyles.tickStyle },
tickCount: 5,
tickLine: false,
dataKey: 'key',
};
export default XAxisWrapper;
Third Method
I didn't like this so I've worked around it, but you can extend the class.
export default class LineWrapper extends Line {
render(){
return (<Line {...this.props} />
}
}
Fourth Method
I don't have a quick example of this, but I always render the shape or children and provide functions to help. For example, for bar cells I use this:
export default function renderBarCellPattern(cellOptions: CellRenderOptions) {
const { data, fill, match, pattern } = cellOptions;
const id = _uniqueId();
const cells = data.map((d) =>
match(d) ? (
<Cell
key={`cell-${id}`}
strokeWidth={4}
stroke={fill}
fill={`url(#bar-mask-pattern-${id})`}
/>
) : (
<Cell key={`cell-${id}`} strokeWidth={2} fill={fill} />
)
);
return !pattern
? cells
: cells.concat(
<CloneElement<MaskProps>
key={`pattern-${id}`}
element={pattern}
id={`bar-mask-pattern-${id}`}
fill={fill}
/>
);
}
// and
<Bar {...requiredProps}>
{renderBarCellPattern(...cell details)}
</Bar>
CloneElement is just a personal wrapper for Reacts cloneElement().

react-select can load async data

I'm trying to build a select component using react-select plugin.
In the process of implementing this project, I have some kind of tricky problem with that. Check out my source code here: https://codesandbox.io/s/j148r99695
The problem that I have is I want to fetch all genresList data from the server and mapping them to select component. But somehow or I do wrong something, It's not working. Please see source code above to help me.
I fetch data from Movies component. Its work well and I pass a props to FormFilter component: <FormFilter genresList={this.state.genres} />. And in the FormFilter component, I check this.props.genresList, it's available. But when I'm trying to assign it to FormFilter state and console.log("state", this.state.genres); that. It's empty. Anyone can tell me why?
Default react-select using value and label to display data to select component. But you know some cases we have to custom that. I try it out by using map to transform to other arrays. But It's the best way? How can I custom valueKey and labelKey.
I'm using react-select beta version2.
UPDATE: I was fixed my project. Please check out the link below. Somehow it's not working. I was commend inside source code.
https://codesandbox.io/s/moym59w39p
So to make it works I have changed the FormFilter.js implementation:
import React, { Component } from "react";
import * as Animated from "react-select/lib/animated";
import AsyncSelect from "react-select/lib/Async";
class FormFilter extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: "",
selectedOption: "",
genres: []
};
}
selectGenreHandleChange = newValue => {
const inputValue = newValue.replace(/\W/g, "");
this.setState({ inputValue });
console.log(inputValue);
};
componentDidMount() {
this.genresOption();
}
filterGenres = inputValue => {
const genres = this.genresOption();
//HERE - return the filter
return genres.filter(genre =>
genre.label.toLowerCase().includes(inputValue.toLowerCase())
);
};
promiseOptions = inputValue => {
return new Promise(resolve => { // HERE - you have to return the promise
setTimeout(() => {
resolve(this.filterGenres(inputValue));
}, 1000);
});
};
genresOption() {
const options = [];
const genres = this.props.genresList.genres; //HERE - array is genres in genresList
if (genres && genres instanceof Array) {
genres.map(genre => options.push({ value: genre.id, label: genre.name}));
}
return options;
}
render() {
const { inputValue } = this.state;
if (this.state.genres) console.log("state", this.state.genres);
if (this.props.genresList)
console.log("Movies props", this.props.genresList);
return (
<div className="filter_form">
<span className="search_element full">
<label htmlFor="genres">Genres</label>
<AsyncSelect
className="select genres"
classNamePrefix="tmdb_select"
isMulti
isSearchable="true"
isClearable="true"
cacheOptions
components={Animated}
value={inputValue}
defaultOptions
onInputChange={this.selectGenreHandleChange}
loadOptions={this.promiseOptions}
/>
</span>
</div>
);
}
}
export default FormFilter;
I have write a comment "HERE - something" to let you know what I changed. There are not big problems :)
I did some changed in your FIDDLE and it's works for me
Something like
import React, {Component} from "react";
import { render } from 'react-dom';
import Movies from './Movies';
import "./styles.css";
class App extends Component {
render() {
return (
<div className="App">
<Movies />
</div>
);
}
}
let a = document.getElementById("root");
render(<App />, a);

Why FlatList is not updating dynamically in React Native

I'm very new to react native and I'm trying to update list dynamically.
Below is my code:
import React, { Component } from "react";
import { View, Text, StyleSheet, FlatList } from "react-native";
import { Tile, ListItem, List } from "react-native-elements";
export default class JoinSession extends Component {
constructor() {
super();
this.state = {
dataToRender: [{ "id": "0", "name": "name0", "des": "des0" }]
}
}
componentDidMount() {
var counter = 0;
const interval = setInterval(() => {
try {
var temp = {
"id": ++counter + "",
"name": "name" + counter,
"des": "des" + counter
}
let tempArray = this.state.dataToRender;
tempArray.push(temp);
this.setState({
dataToRender: tempArray
});
console.log(this.state.dataToRender);
if(counter === 10) {
clearInterval(interval);
}
} catch (e) {
console.log(e.message);
}
}, 2000);
}
renderList(item) {
console.log(item);
return (
<ListItem
roundAvatar
title={item.name}
subtitle={item.des}
/>
);
}
render() {
return (
<View style={{ flex: 1, alignItems: "stretch", backgroundColor: "skyblue" }}>
<List>
<FlatList
data={this.state.dataToRender}
renderItem={({ item }) => this.renderList(item)}
keyExtractor={item => item.id}
/>
</List>
</View>
);
}
}
I am only able to get first element which I've declared in the constructor but data which I'm appending in serInterval is not showing up on the page.
Please help me to resolve it or if there is any other way to do it, please let me know.
Thanks in advance.
Hi try to have a look on the extraData parameter you can use on a FlatList:
By passing extraData={this.state} to FlatList we make sure FlatList itself will re-render when the state.selected changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is also a PureComponent and the prop comparison will not show any changes.
<FlatList
...
extraData={this.state}
/>
More info on the FlatList documentation: https://facebook.github.io/react-native/docs/flatlist.html
Also you shouldn't need this <List> from react native element here the list behaviour is totally handle by your FlatList.
Like say #AlexDG Flat list is Pure component. For updating pure component use key prop.
But do not overdo it, otherwise you can get unnecessary redrawing.
<FlatList
key={this.state.dataToRender.length} <---------- rerender
data={this.state.dataToRender}
renderItem={({ item }) => this.renderList(item)}
keyExtractor={item => item.id}
/>
I just had this on my own and just so happened to read this comment by OP:
The problem was array mutation. Never mutate array or object in react native.
And I indeed changed my state this way:
this.setState(prev =>
prev.listData.push("stuff");
return prev;
});
And you can see it in the question as well:
let tempArray = this.state.dataToRender;
tempArray.push(temp);
After changing it to
this.setState(prev => {
let copy = Array.from(prev.listData);
copy.push("stuff");
return {listData: copy};
});
however, my list was updating just fine!
So if you are mutating an array in your state that is related to your list's data you might want to see if this helps some.

How to call navigate from a component rendered at top level

According to the docs on react-navigation you can call navigate from the top level component using the following:
import { NavigationActions } from 'react-navigation';
const AppNavigator = StackNavigator(SomeAppRouteConfigs);
class App extends React.Component {
someEvent() {
// call navigate for AppNavigator here:
this.navigator && this.navigator.dispatch(
NavigationActions.navigate({ routeName: someRouteName })
);
}
render() {
return (
<AppNavigator ref={nav => { this.navigator = nav; }} />
);
}
}
However I'm trying to figure out how can this be done if the logic that does the dispatch is in another component that is rendered on the same level as the navigator? In my case I create my navigators (A drawer with a stack navigator and other nested navigators) and then I render them using the <Drawer>. On the same level I'm loading my <PushController> component to handle push notifications. The pushcontroller actually gets the event that I want to dispatch on.
I can't figure out how to pass(?) the ref to the pushcontroller component so I can use it, currently the following isn't working. I get the console log telling me that the fcm.ACTION.OPEN_NOTIFICATION triggered but no dispatch occurs. I suppose it could be because the ref is created during a render and it isn't available to pass yet when the render occurs? But I'm also not sure you would do things this way in order to give another component access to a ref declared at the same level. Thanks for your help in advance!
Drawer + PushController rendering
render(){
return(
<View style={{flex: 1}}>
<Drawer ref={nav => { this.navigator = nav; }}/>
<PushController user={this.props.user} navigator={this.navigator}/>
</View>
)
}
PushController snippet:
import { NavigationActions } from 'react-navigation';
async doFCM() {
FCM.getInitialNotification().then(notif => {
console.log('Initial Notification', notif);
if(notif.fcm.action === "fcm.ACTION.OPEN_NOTIFICATION"){
console.log('fcm.ACTION.OPEN_NOTIFICATION triggered', notif);
this.props.navigator && this.props.navigator.dispatch(NavigationActions.navigate({routename: 'Chat'}))
}
});
}
Answer was to move the navigate call to a function defined at render and pass it to the component.
import { NavigationActions } from 'react-navigation';
...
//class definition omitted for brevity
render(){
const callNavigate = (routeName, params) => {
console.log('Params', params);
this.navigator.dispatch({
type: NavigationActions.NAVIGATE,
routeName: routeName,
params: params
})
}
return(
<View style={{flex: 1}}>
<Drawer ref={nav => this.navigator = nav }/>
<PushController callNavigate={callNavigate}/>
</View>
)
}
The function is called within PushController like this:
this.props.callNavigate('RouteName', params);

Categories

Resources