I want to have 2 functional React components:
a Data
component that holds a value in state, that is passed through props:
a SummedValue
that also holds a value in state, but calculates this value as the sum of it's children's value, where its children can be either Data
or other SummedValue
components, nested arbitrarily
I would like to use them like this:
<SummedValue>
<Data value={3}/>
<Data value={5}/>
<SummedValue>
<Data value={4}/>
<Data value={1}/>
</SummedValue>
Currently, in the SummedValue
component, I can get the data from Data
components alone by children.props.value
. However I dont know how to get this value from nested SummedValue
components, as it is not a prop.
Basically I think this is related to passing data from the child to the parent, but I cant find the configuration that works
function SummedValue({children}) {
// how can I access this computed value in parent component?
let computedValue = 0;
// Only works for props, not internal values
children.forEach(child => {
if (child.props.value) {
value += child.props.value
}
})
return(
<div>
<span>SUMMED NUMBER IS: {computedValue}
{children}
</div>
)
}
As we know Context passes through intermediate components while Using and providing context from the same component. The sample code below follows the same principle, please see the output below prior to proceeding with the code.
Note : This is a naive code, please see the another answer, the code in it is handling the known edge cases as well.
Please take advantage of the comments enclosed in the code.
App.js
import { useContext } from 'react';
import { RunningSectionSumContext } from './context';
export default function App() {
return (
<>
<SummedValue>
<Data value={10} />
<Data value={5} />
<SummedValue>
<Data value={20} />
<Data value={30} />
</SummedValue>
</SummedValue>
</>
);
}
function SummedValue({ children }) {
// Destructuring nested objects.
const sectionSum = children
// filter the children with value, the Data components only
.filter(({ props: { value } }) => value)
.map(({ props: { value } }) => value)
.reduce((a, b) => a + b, 0); // find the section sum
// the statement using the context.
const runningSectionSum = useContext(RunningSectionSumContext) + sectionSum;
return (
<div
style={{
border: '2px dotted white',
margin: 10,
padding: 10,
width: '50%',
}}
>
<strong>Running section sum is : {runningSectionSum}</strong>
<!-- the statement providing the new value to the context -->
<RunningSectionSumContext.Provider value={runningSectionSum}>
<!-- while evaluating the children below, the same component
will be invoked recursively if the child component is
is a nested SummedValue component. -->
{children}
</RunningSectionSumContext.Provider>
<br />
</div>
);
}
function Data({ value }) {
return (
<>
<br />
<em>data value : {value}</em>
</>
);
}