Recharts: Change z-index of tick text, so that text shows on top of bars - recharts

You can see in the example above that the bars are on top of the text. I am currently solving for this by adjusting the opacity of the bars, but I would really like to just have the text sit on top of the bars.
I have tried using props in the Bar/tick components, as well as using the style attributes, but neither have worked.
Here is my code:
import { FaLock } from "react-icons/fa";
import {
BarChart,
Bar,
Cell,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
Label,
} from "recharts";
export const MonthlyResults = ({ data }) => {
const CustomTick = (props) => {
const { x, y, payload } = props;
return (
<text
x={x + 20}
y={y + 5}
fontWeight={900}
fill="blue"
z={100}
style={{ fontSize: "1.5rem", zIndex: 100 }}
>
{payload.value.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase();
})}{" "}
({data[payload.index].count})
</text>
);
};
return (
<div className="min-h-fit w-full justify-center bg-gray-200 rounded-md">
<h1 className="text-center text-2xl my-4 font-semibold">
Top 50 Restaurants in NYC
</h1>
<div className="h-[2000px] w-full">
<ResponsiveContainer>
<BarChart
data={data}
layout="vertical"
margin={{ top: 0, right: 0, left: -61, bottom: 0 }}
barCategoryGap="1"
>
<XAxis type="number" hide domain={[0, "dataMax"]} />
<YAxis
yAxisId={0}
type="category"
dataKey="name"
tick={<CustomTick />}
style={{
fontSize: "1rem",
fontWeight: "bold",
}}
/>
<Tooltip content={() => null} />
<Bar
dataKey="count"
onClick={(e) => {
console.log(e);
}}
style={{ zIndex: 50 }}
z={50}
>
{data.map((entry, index) => (
<Cell
cursor="pointer"
key={`cell-${index}`}
fill="rgba(112, 207, 250, 1)"
/>
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
</div>
);
};

Related

Adding Leading Zero's in React 'duration' component

I have created a 'working' duration slider, but I'm having trouble inserting 'leading zeros' into the hours and minutes:
const [hours, setHours] = useState([parseFloat(defaultValue.split(':')[0])])
const [minutes, setMinutes] = useState([parseFloat(defaultValue.split(':')[1])])
The above code is working, but will output (9hours & 5mins) as 9:5 (needs to be 09:05)
The code below (commented out in the full code) successfully puts in the leading zero's, but the code errors on line 61 and 97 where it's trying to handle the value
var leadingZeroHour = ('0000'+[parseFloat(defaultValue.split(':')[0])]).slice(-2)
const [hours, setHours] = useState(leadingZeroHour)
var leadingZeroMin = ('0000'+[parseFloat(defaultValue.split(':')[1])]).slice(-2)
const [minutes, setMinutes] = useState(leadingZeroMin)
The full code is below. If you can help me out and point me in the right direction, I'd be very grateful. Many thanks!
import React, { FunctionComponent, useState, useEffect } from 'react'
import { Range } from 'react-range'
type Props = {
className?: string,
defaultValue: string,
title?: string,
onUpdate: (value: string) => void;
[x: string]: any;
}
const defaultProps: Props = {
className: '',
defaultValue: '00:00',
title: '',
onUpdate: (value: any) => {},
}
const DurationInput: FunctionComponent<Props> = ({ className, defaultValue, title, onUpdate, ...rest }) => {
const [hours, setHours] = useState([parseFloat(defaultValue.split(':')[0])])
const [minutes, setMinutes] = useState([parseFloat(defaultValue.split(':')[1])])
// var leadingZeroHour = ('0000'+[parseFloat(defaultValue.split(':')[0])]).slice(-2)
// const [hours, setHours] = useState(leadingZeroHour)
// var leadingZeroMin = ('0000'+[parseFloat(defaultValue.split(':')[1])]).slice(-2)
// const [minutes, setMinutes] = useState(leadingZeroMin)
// console.log(hours)
useEffect(() => {
const duration = `${hours[0]}:${minutes[0]}`
onUpdate(duration)
}, [hours, minutes])
return (
<div className={`w-full ${className}`}>
{title ? <div className="text-base sm:text-xl mb-4 text-center">{title}</div> : <></>}
{/* <div className="grid grid-cols-3 gap-3 mb-8">
<div></div> */}
<div className="mx-auto w-40 sm:w-80 mb-8">
<div className="border border-orange rounded py-3 text-center text-4xl bg-white">
{hours}:{minutes}
</div>
<div></div>
</div>
<div className="mb-4 w-4/5 sm:w-3/5 mx-auto">
<div className="font-bold text-center mb-6">Hours</div>
<Range
step={1}
min={0}
max={23}
values={hours}
onChange={(values) => setHours(values)}
renderTrack={({ props, children }) => (
<div
{...props}
className="bg-orange rounded-full"
style={{
...props.style,
height: '6px',
width: '100%',
}}
>
{children}
</div>
)}
renderThumb={({ props }) => (
<div
{...props}
className="bg-white rounded-full border-4 border-orange"
style={{
...props.style,
height: '42px',
width: '42px',
}}
/>
)}
/>
</div>
<div className=" w-4/5 sm:w-3/5 mx-auto">
<div className="font-bold text-center mb-6">Minutes</div>
<Range
step={5}
min={0}
max={59}
values={minutes}
onChange={(values) => setMinutes(values)}
renderTrack={({ props, children }) => (
<div
{...props}
className="bg-orange rounded-full"
style={{
...props.style,
height: '6px',
width: '100%',
}}
>
{children}
</div>
)}
renderThumb={({ props }) => (
<div
{...props}
className="bg-white rounded-full border-4 border-orange"
style={{
...props.style,
height: '42px',
width: '42px',
}}
/>
)}
/>
</div>
</div>
)
}
DurationInput.defaultProps = defaultProps
export default DurationInput

How to use PointerLockControls with useFrame

I will implement an effect that I can move camera around an object.
Error example is this and I need to fix it. The difference between this and the latter is that this can update camera position to a 3D object in a model. This example eventually can move camera around a 3D object after updating camera position. But if I follow general use case, i.e. the latter, it can't function correctly. I wonder what's difference between this and the latter.
function Button(props) {
const vec = new THREE.Vector3(0, 3, 5);
const { x, y, z } = props.position;
if (x && y && z) {
vec.set(x, y, z);
}
useFrame((state) => {
const step = 0.1;
state.camera.position.lerp(vec, step);
state.camera.lookAt(0, 0, 0);
state.camera.updateProjectionMatrix();
});
return null;
}
const ModelCanvas = (props) => {
return (
<div className="center">
<Canvas
shadowMap
style={{ background: "#ffffff" }}
camera={{ position: [0, 3, 10], fov: 100 }}
gl={{ antialias: false, logarithmicDepthBuffer: true }}
id="my-canvas"
>
<ambientLight intensity={0.5} />
<pointLight position={[0, 60, -100]} intensity={20} />
<pointLight position={[-50, 0, -50]} intensity={2} />
<spotLight
castShadow
intensity={8}
angle={Math.PI / 10}
position={[10, 10, 10]}
shadow-mapSize-width={2048}
shadow-mapSize-height={2048}
/>
<Suspense fallback={null}>
<Model url={"/compressed.glb"} />
</Suspense>
{/* <PointerLockControls /> */}
<Button position={props.value} />
<PointerLockControls />
</Canvas>
</div>
);
};
And this example functions correctly.
const ModelCanvas = (props) => {
return (
<div className="center">
<Canvas
shadowMap
style={{ background: "#ffffff" }}
camera={{ position: [0, 3, 10], fov: 100 }}
gl={{ antialias: false, logarithmicDepthBuffer: true }}
id="my-canvas"
>
<ambientLight intensity={0.5} />
<pointLight position={[0, 60, -100]} intensity={20} />
<pointLight position={[-50, 0, -50]} intensity={2} />
<spotLight
castShadow
intensity={8}
angle={Math.PI / 10}
position={[10, 10, 10]}
shadow-mapSize-width={2048}
shadow-mapSize-height={2048}
/>
<Suspense fallback={null}>
<Model url={"/compressed.glb"} />
</Suspense>
{/* <PointerLockControls /> */}
{/* <Button position={props.value} /> */}
<PointerLockControls />
</Canvas>
</div>
);
};

How to setField value on Multistep form using formik?

I am trying to create multistep form using formik but i am not able to
set form value when on click serach button. when i click search button
then i am set the data and i want to auto field make and model field
how can i do ????
here is my stepper.js page--
const renderStep = (
step,
values,
SearchButtonClick,
setFieldValue,
vehicleData
) => {
switch (step) {
case 1:
return <Form1 values={values} />;
case 2:
return (
<Form2
values={values}
SearchButtonClick={SearchButtonClick}
setFieldValue={setFieldValue}
vehicleData={vehicleData}
/>
);
default:
return <Form1 values={values} />;
}
};
const Stepper = () => {
const [step, setStep] = useState(1);
const [vehicleData, setVehicleData] = useState({});
function SearchButtonClick(data) {
const value={
"vehicleId": "V1",
"rcNo": "OD01AB1234",
"chassisNo": "ODXYZ123",
"engineNo": "ENABC012",
"make": "HYUNDAI MOTOR INDIA LTD ",
"model": "HYUNDAI SANTRO",
"fuel": "PETROL",
"vehicle_type": "Motor Car",
"pollutionNorms": "Not Available",
"mfgYear": 2017
}
setVehicleData(value)
}
const formData = {
PolicystartDate: "",
PolicyendDate: "",
regNo: "OD01AB1234",
make: "",
model: "",
};
function handleSubmit(values) {
debugger;
setStep((step) => step + 1);
console.log({
...values,
PolicystartDate: moment(values.PolicystartDate).toISOString(),
PolicyendDate: moment(values.PolicyendDate).toISOString(),
});
}
return (
<MainWrapper>
<>
{/* <Header title="Multi Step Form using React And Formik" /> */}
<Formik
enableReinitialize
initialValues={{ ...formData }}
onSubmit={handleSubmit}
>
{({
errors,
touched,
isSubmitting,
setFieldValue,
setFieldTouched,
values,
...rest
}) => (
<Form>
{renderStep(
step,
values,
SearchButtonClick,
setFieldValue,
vehicleData
)}
<StepButton step={step} />
</Form>
)}
</Formik>
</>
</MainWrapper>
);
};
export default Stepper;
here is my first Form ----
function Form1(props) {
console.log(props);
return (
<>
<PageHeader
style={{
border: "1px solid rgb(235, 237, 240)",
}}
title="Cover"
/>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div
style={{
height: "100%",
width: "30%",
marginLeft: "15px",
}}
>
<Field
name="PolicystartDate"
label="Policy Start Date"
component={DatePicker}
isColumn
width={"100%"}
value={props.values.PolicystartDate}
inlineLabel
style={{
flexBasis: "80%",
height: "33px",
width: "100%",
marginTop: "4px",
}}
/>
<Spacer />
<Field
name="PolicyendDate"
label="Policy End Date"
component={DatePicker}
isColumn
width={"100%"}
value={props.values.PolicyendDate}
inlineLabel
style={{
flexBasis: "80%",
height: "33px",
width: "100%",
marginTop: "4px",
}}
/>
<Spacer />
</div>
</div>
</>
);
}
export default Form1;
here is my second form-------
function Form2(props) {
console.log(props);
return (
<>
<StyledLabel>Registration Number</StyledLabel>
<Field
name="regNo"
component={InputComponent}
isColumn
isColumn
style={{
height: "33px",
width: "80%",
}}
/>
<Button
style={{ marginTop: "21px" }}
onClick={() =>
props.SearchButtonClick(props.values.regNo, props.setFiledValue)
}
>
Search
</Button>
<Field
disabled="true"
name="make"
label="Make"
width={"100%"}
isRequired
isColumn
component={InputComponent}
style={{
flexBasis: "80%",
height: "33px",
marginTop: "4px",
}}
/>
<Field
disabled="true"
name="model"
label="Model"
width={"100%"}
isRequired
isColumn
component={InputComponent}
style={{
flexBasis: "80%",
height: "33px",
marginTop: "4px",
}}
/>
export default Form2
set Fields value on Form 2 from props , same as you have setted in Form1
function Form2(props) {
console.log(props);
return (
<>
<StyledLabel>Registration Number</StyledLabel>
<Field
name="regNo"
component={InputComponent}
isColumn
isColumn
style={{
height: "33px",
width: "80%",
}}
/>
<Button
style={{ marginTop: "21px" }}
onClick={() =>
props.SearchButtonClick(props.values.regNo, props.setFiledValue)
}
>
Search
</Button>
<Field
disabled="true"
name="make"
label="Make"
width={"100%"}
isRequired
isColumn
value={props.values.make}
component={InputComponent}
style={{
flexBasis: "80%",
height: "33px",
marginTop: "4px",
}}
/>
<Field
disabled="true"
name="model"
label="Model"
width={"100%"}
isRequired
isColumn
value={props.values.model}
component={InputComponent}
style={{
flexBasis: "80%",
height: "33px",
marginTop: "4px",
}}
/>
export default Form2

Why is react-native-material-dropdown so slow in the way I use it to edit and add majors?

My app drop-downs are pretty slow. It especially takes a long time if i go into the Settings Screen and try to change my major. Or anything dealing with adding or editing a major in this specific screen. I really need to make adding or editing a major much faster. I am not sure the reason why editing or adding a major is taking too long. And I am not sure about how to go at making adding and editing a major much faster. So I am posting the entire component and I am commenting parts that deal with adding or editing a major. I am using react-native-material-dropdown.
Here is the entire component code:
import React from 'react';
import PropTypes from 'prop-types';
import { View, ScrollView, Text, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { compose, withStateHandlers } from 'recompose';
import { Dropdown } from 'react-native-material-dropdown';
import { Icon } from 'react-native-material-ui';
import R from 'ramda';
import { ConnectivityRenderer } from 'react-native-offline';
import NetworkConnectivity from '../error/NetworkConnectivity';
import { toArray } from '../selectors';
import { Container, Switch, SwitchOption } from './common';
import { editStudent } from '../actions';
const propTypes = {
toolbar: PropTypes.elem,
loading: PropTypes.bool,
university: PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string,
}),
universities: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string,
})
),
degrees: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string,
})
),
studentDegrees: PropTypes.arrayOf(
PropTypes.shape({
index: PropTypes.number,
track: PropTypes.string,
})
),
errors: PropTypes.shape({
university: PropTypes.string,
degree: PropTypes.string,
}),
year: PropTypes.string,
onUniversityChange: PropTypes.func,
onTermChange: PropTypes.func,
onDegreeChange: PropTypes.func,
onYearChange: PropTypes.func,
onTrackChange: PropTypes.func,
onAddDegree: PropTypes.func,
onDone: PropTypes.func,
};
const contextTypes = {
uiTheme: PropTypes.object.isRequired,
};
const validate = state => {
const result = {};
if (!state.university.id) {
result.university = 'You should select an university';
}
return result;
};
const enhance = compose(
connect(
({ user, universities, degrees }) => ({
studentId: user.id,
user,
universities: toArray(universities),
degrees: toArray(degrees),
}),
{ editStudent }
),
withStateHandlers(
props => {
return {
university: props.user.university || {},
year: props.user.academicClass || 'freshman',
studentDegrees: R.isEmpty(props.degrees)
? []
: R.isEmpty(props.user.studentDegrees)
? [{ degree_id: props.degrees[0].id, track: 'Major' }]
: R.values(props.user.studentDegrees),
errors: {},
};
},
{
onUniversityChange: () => (value, index, data) => ({
university: data[index],
}),
onYearChange: () => year => ({ year }),
onTrackChange: state => ({ idx, track }) => ({
studentDegrees: R.update(
idx,
R.assoc('track', track, state.studentDegrees[idx]),
state.studentDegrees
),
}),
// Fucntion dealing with degree change
onDegreeChange: (state, props) => ({ idx, index }) => ({
studentDegrees: R.update(
idx,
R.assoc(
'degree_id',
props.degrees[index].id,
state.studentDegrees[idx]
),
state.studentDegrees
),
}),
// Function dealing with degree adding
onAddDegree: (state, props) => () => ({
studentDegrees: R.append(
{
degree_id: props.degrees[0].id,
track: 'Major',
},
state.studentDegrees
),
}),
onRemoveDegree: state => idx => ({
studentDegrees: [
...state.studentDegrees.slice(0, idx),
...state.studentDegrees.slice(idx + 1),
],
}),
// When the user is done with settings.
// This function communicates with the back end to save things in the remote database
onDone: (state, { studentId, editStudent }) => () => {
const errors = validate(state);
if (Object.keys(errors).length !== 0) {
return { errors };
}
editStudent(
studentId,
state.year,
state.university.id,
state.studentDegrees
);
},
}
)
);
// The Settings Component
const FormUserSettings = (props, context) => {
const styles = getStyles(props, context);
return (
<ConnectivityRenderer>
{isConnected => (
isConnected ? (
<Container>
{React.cloneElement(props.toolbar, {
onRightElementPress: props.onDone,
})}
<ScrollView style={styles.container}>
<Text
style={[
styles.title,
props.errors.university ? styles.titleError : {},
]}
>
University
</Text>
// Selecting a university
<Dropdown
label="Select university..."
data={props.universities.map(u => ({ id: u.id, value: u.name }))}
onChangeText={props.onUniversityChange}
value={props.university.name}
/>
{props.errors.university &&
<Text style={styles.errorMessage}>
{props.errors.university}
</Text>}
<View style={{ height: 16 }} />
<Text style={styles.title}>Current Year</Text>
<View style={{ height: 8 }} />
<Switch
value={props.year}
onChange={props.onYearChange}
selectedColor={styles.switchSelectedColor}
unselectedColor={styles.switchUnselectedColor}
>
<SwitchOption text="Freshman" value="freshman" />
<SwitchOption text="Sophomore" value="sophomore" />
<SwitchOption text="Junior" value="junior" />
<SwitchOption text="Senior" value="senior" />
</Switch>
<View style={{ height: 16 }} />
<Text
style={[styles.title, props.errors.degree ? styles.titleError : {}]}
>
Major / Minors
</Text>
{!R.isEmpty(props.degrees) &&
props.studentDegrees.map((sd, idx) => {
const degree = R.find(R.propEq('id', sd.degree_id), props.degrees);
return (
<View
key={`sd-${idx}`}
style={{ flex: 1, height: 96, marginTop: 24 }}
>
<View
style={{
flex: 1,
flexDirection: 'row',
alignItems: 'flex-end',
}}
>
<View style={{ flex: 1 }}>
<Dropdown
style={{ flex: 1 }}
label="Select degree..."
data={props.degrees.map(d => ({
id: d.id,
value: d.name,
}))}
onChangeText={(value, index) =>
props.onDegreeChange({ idx, index })}
value={degree ? degree.name : ''}
/>
</View>
{props.studentDegrees.length !== 1 &&
<TouchableOpacity
style={{ marginBottom: 8, paddingLeft: 24 }}
onPress={() => props.onRemoveDegree(idx)}
>
<Icon name="delete" size={24} />
</TouchableOpacity>}
</View>
<Switch
value={sd.track}
onChange={track => props.onTrackChange({ idx, track })}
selectedColor={styles.switchSelectedColor}
unselectedColor={styles.switchUnselectedColor}
>
<SwitchOption text="Major" value="Major" />
<SwitchOption text="Minor" value="Minor" />
<SwitchOption text="Certificate" value="Cert" />
</Switch>
</View>
);
})}
<TouchableOpacity style={{ padding: 10 }} onPress={props.onAddDegree}>
<Text style={styles.addDegreeText}>+ Degree</Text>
</TouchableOpacity>
</ScrollView>
</Container>
) : (
<Container>
{React.cloneElement(props.toolbar, {
onRightElementPress: props.onDone,
})}
<NetworkConnectivity />
<ScrollView style={styles.container}>
<Text
style={[
styles.titleDisabled,
props.errors.university ? styles.titleError : {},
]}
>
University
</Text>
<Dropdown
label=""
data={props.universities.map(u => ({ id: u.id, value: u.name }))}
onChangeText={props.onUniversityChange}
value={props.university.name}
disabled={true}
editable={false}
/>
{props.errors.university &&
<Text style={styles.errorMessage}>
{props.errors.university}
</Text>}
<View style={{ height: 16 }} />
<Text style={styles.titleDisabled}>Current Year</Text>
<View style={{ height: 8 }} />
<Switch
value={props.year}
onChange={props.onYearChange}
selectedColor={styles.disabledSwitchSelectedColor}
unselectedColor={styles.switchUnselectedColor}
>
<SwitchOption text="Freshman" value="freshman" />
<SwitchOption text="Sophomore" value="sophomore" />
<SwitchOption text="Junior" value="junior" />
<SwitchOption text="Senior" value="senior" />
</Switch>
<View style={{ height: 16 }} />
<Text
style={[styles.titleDisabled, props.errors.degree ? styles.titleError : {}]}
>
Major / Minors
</Text>
// The problem of slowness starts here
// I feel like something here should be improved
{!R.isEmpty(props.degrees) &&
props.studentDegrees.map((sd, idx) => {
const degree = R.find(R.propEq('id', sd.degree_id), props.degrees);
return (
<View
key={`sd-${idx}`}
style={{ flex: 1, height: 96, marginTop: 24 }}
>
<View
style={{
flex: 1,
flexDirection: 'row',
alignItems: 'flex-end',
}}
>
<View style={{ flex: 1 }}>
<Dropdown
style={{ flex: 1 }}
label="Select degree..."
data={props.degrees.map(d => ({
id: d.id,
value: d.name,
}))}
disabled={true}
editable={false}
onChangeText={(value, index) =>
props.onDegreeChange({ idx, index })}
value={degree ? degree.name : ''}
/>
</View>
</View>
<Switch
value={sd.track}
onChange={track => props.onTrackChange({ idx, track })}
selectedColor={styles.disabledSwitchSelectedColor}
unselectedColor={styles.switchUnselectedColor}
>
<SwitchOption text="Major" value="Major" />
<SwitchOption text="Minor" value="Minor" />
<SwitchOption text="Certificate" value="Cert" />
</Switch>
</View>
);
})}
<TouchableOpacity disabled={true} style={{ padding: 10 }} onPress={props.onAddDegree} disabled={true}>
<Text style={styles.addDegreeTextDisabled}>+ Degree</Text>
</TouchableOpacity>
</ScrollView>
</Container>
)
)}
</ConnectivityRenderer>
);
};
FormUserSettings.contextTypes = contextTypes;
FormUserSettings.propTypes = propTypes;
export default enhance(FormUserSettings);

React Native sticky row and header scroll performance?

I have cobbled together a working version of a Microsoft Excel like "freeze pains" view. The column header scrolls with the content horizontally and the row headers scroll with the content vertically but each is "stuck" in position when the other is scrolled.
You can try the working version here.
It's not optimal as it stutters if you stop a flicked scroll or just swipe around a lot.
The approach uses a couple techniques but the one causing the issue is the synced scroll view.
As outlined here, I've tried setting useNativeDriver: true, which necessitates changing
ScrollView to Animated.ScrollView and
ref={ref => (this.instance = ref)} to ref={ref => (this.instance = ref._component)}
but then the synced goes completely haywire.
I'd love ideas on a more optimal approach. How can this be improved?
import React from 'react';
import { ScrollView, Animated, Text, View } from 'react-native';
export default class SyncScrollTest extends React.Component {
constructor() {
super();
this.scrollPosition = new Animated.Value(0);
this.scrollEvent = Animated.event(
[{ nativeEvent: { contentOffset: { y: this.scrollPosition } } }],
{ useNativeDriver: false },
);
}
render() {
return (
<View style={{ flex: 1 }}>
<View style={{ flexDirection: 'row' }}>
<ScrollViewVerticallySynced
style={{ width: 50, marginTop: 60 }}
name="C1"
color="#F2AFAD"
onScroll={this.scrollEvent}
scrollPosition={this.scrollPosition}
/>
<ScrollView horizontal bounces={false}>
<View style={{ width: 600 }}>
<View style={{ height: 60, justifyContent: 'center', backgroundColor: '#B8D2EC' }}>
<Text>
I am Column Header!! I am Column Header!! I am Column Header!! I am Column
Header!! I am Column Header!! I am Column Header!! I am Column Header!!
</Text>
</View>
<ScrollViewVerticallySynced
style={{ width: 600 }}
name="C2"
color="#D9E4AA"
onScroll={this.scrollEvent}
scrollPosition={this.scrollPosition}
/>
</View>
</ScrollView>
</View>
</View>
);
}
}
class ScrollViewVerticallySynced extends React.Component {
componentDidMount() {
this.listener = this.props.scrollPosition.addListener((position) => {
this.instance.scrollTo({
y: position.value,
animated: false,
});
});
}
render() {
const { name, color, style, onScroll } = this.props;
return (
<ScrollView
key={name}
ref={ref => (this.instance = ref)}
style={style}
scrollEventThrottle={1}
onScroll={onScroll}
bounces={false}
showsVerticalScrollIndicator={false}
>
{someRows(name, 25, color)}
</ScrollView>
);
}
}
const someRows = (name, rowCount, color) =>
Array.from(Array(rowCount).keys()).map(index =>
(<View
key={`${name}-${index}`}
style={{
height: 50,
backgroundColor: index % 2 === 0 ? color : 'white',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>
{name} R{index + 1}
</Text>
</View>),
);
```
I've changed your example, instead of using listeners and Animated Event I use the scrollTo method from ScrollView to synchronize the scrolling. I think that listeners are the cause of lag between the rows when you are scrolling.
You can test the changes here.
import React from 'react';
import { ScrollView, Text, View } from 'react-native';
import { Constants } from 'expo'
export default class SyncScrollTest extends React.Component {
constructor() {
super();
this.c1IsScrolling = false;
this.c2IsScrolling = false;
}
render() {
return (
<View style={{ flex: 1, marginTop: Constants.statusBarHeight }}>
<View style={{ flexDirection: 'row' }}>
<ScrollViewVerticallySynced
style={{ width: 50, marginTop: 60 }}
refe= {ref => (this.c2View = ref)}
name="C1"
color="#F2AFAD"
onScroll={e => {
if (!this.c1IsScrolling) {
this.c2IsScrolling = true;
var scrollY = e.nativeEvent.contentOffset.y;
this.c1View.scrollTo({ y: scrollY });
}
this.c1IsScrolling = false;
}}
/>
<ScrollView horizontal bounces={false}>
<View style={{ width: 400 }}>
<View style={{ height: 60, justifyContent: 'center', backgroundColor: '#B8D2EC' }}>
<Text>
I am Column Header!! I am Column Header!! I am Column Header!! I am Column
Header!! I am Column Header!! I am Column Header!! I am Column Header!!
</Text>
</View>
<ScrollViewVerticallySynced
style={{ width: 400 }}
refe= {ref => (this.c1View = ref)}
name="C2"
color="#D9E4AA"
onScroll= {e => {
if (!this.c2IsScrolling) {
this.c1IsScrolling = true;
var scrollY = e.nativeEvent.contentOffset.y;
this.c2View.scrollTo({ y: scrollY });
}
this.c2IsScrolling = false;
}}
/>
</View>
</ScrollView>
</View>
</View>
);
}
}
class ScrollViewVerticallySynced extends React.Component {
render() {
const { name, color, style, onScroll, refe } = this.props;
return (
<ScrollView
key={name}
ref={refe}
style={style}
scrollEventThrottle={1}
onScroll={onScroll}
bounces={false}
showsVerticalScrollIndicator={false}
>
{someRows(name, 25, color)}
</ScrollView>
);
}
}
const someRows = (name, rowCount, color) =>
Array.from(Array(rowCount).keys()).map(index =>
(<View
key={`${name}-${index}`}
style={{
height: 50,
backgroundColor: index % 2 === 0 ? color : 'white',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>
{name} R{index + 1}
</Text>
</View>),
);
You can find another example here

Resources