New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

`combineReducers` doc does not explain how t gueses which part of state to pass to reducer #428

Closed
skfd opened this Issue Aug 8, 2015 · 6 comments

Comments

3 participants
@skfd

skfd commented Aug 8, 2015

New combineReducers doc is ok, but it does not explain that you have to name reducer as the party of the state it manages.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 8, 2015

Contributor

It kinda says it if you read it completely, without skipping:

The resulting reducer calls every child reducer, and gather their results into a single state object. The shape of the state object matches the keys of the passed reducers.

But I agree this needs to be more prominent, as people often miss this sentence.
Would changes from #399 help you?

Contributor

gaearon commented Aug 8, 2015

It kinda says it if you read it completely, without skipping:

The resulting reducer calls every child reducer, and gather their results into a single state object. The shape of the state object matches the keys of the passed reducers.

But I agree this needs to be more prominent, as people often miss this sentence.
Would changes from #399 help you?

@gaearon gaearon added the docs label Aug 8, 2015

@skfd

This comment has been minimized.

Show comment
Hide comment
@skfd

skfd Aug 9, 2015

I`d prefer less computer sciency, explicit note about this convention right after example:

Note that reducers are named todos and counter -- exactly as the parts of the state we're passing to them.

To pass part of the state to reducer Redux employs convention -- reducer should be named exactly as a part of the state you wish to pass to it.

I think #399 is good but it's an explanation for Flux users. I'm coming to Redux fresh of the boat. Some form of my text would be more helpful for casual JavaScript developers.

Important idea here is: Convention are part of API. It's equally important to createRedux, createDispatcher and any other API function. Always explicitly state conventions because they are a part of API.

Btw, I'm still puzzled how to pass subpart of state to reducer. Should I camelCase join them or something? state={owner: {name: 'John'} }export function ownerName(state = [], action)?

skfd commented Aug 9, 2015

I`d prefer less computer sciency, explicit note about this convention right after example:

Note that reducers are named todos and counter -- exactly as the parts of the state we're passing to them.

To pass part of the state to reducer Redux employs convention -- reducer should be named exactly as a part of the state you wish to pass to it.

I think #399 is good but it's an explanation for Flux users. I'm coming to Redux fresh of the boat. Some form of my text would be more helpful for casual JavaScript developers.

Important idea here is: Convention are part of API. It's equally important to createRedux, createDispatcher and any other API function. Always explicitly state conventions because they are a part of API.

Btw, I'm still puzzled how to pass subpart of state to reducer. Should I camelCase join them or something? state={owner: {name: 'John'} }export function ownerName(state = [], action)?

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 9, 2015

Contributor

Thanks for feedback, it's very valuable.

I want to stress that

Convention are part of API

is not really true.

We should probably just avoid import * in the docs because people assume it's integral part of the API when it's not at all, and it's just a convenient shortcut I use.

Function names only matter because of how ES6 export and import * as work. They have nothing to do with combineReducers API per se.

Btw, I'm still puzzled how to pass subpart of state to reducer. Should I camelCase join them or something? state={owner: {name: 'John'} } → export function ownerName(state = [], action)?

No :-). It's not a magical API. combineReducers(object) combines several reducers into one, and passes parts of state to its values by the keys you provide. It only does this exactly one level deep. There's no “build whole tree magic”. It's up to you to split a reducer into more functions:

// Function names don't matter!
function processA(state, action) { ... }
function doSomethingWithB(state, action) { ... }

function reducer(state = {}, action) {
  return {
    // Because you call them!
    a: processA(state.a, action),
    b: doSomethingWithB(state.b, action)
  };
}

// Not using combineReducers
let store = createStore(reducer);

They don't even matter if you use combineReducers helper as long as you create the object yourself:

// Function names don't matter!
function processA(state, action) { ... }
function doSomethingWithB(state, action) { ... }

/*
function reducer(state = {}, action) {
  return {
    a: processA(state.a, action),
    b: doSomethingWithB(state.b, action)
  };
}
*/
let reducer = combineReducers({
  a: processA,
  b: doSomethingWithB
});

let store = createStore(reducer);

You can do this many times.

Before:

// Function names don't matter!
function processSomePartOfA(state, action) { ... }
function doSomethingWithOtherPartOfA(state, action) { ... }

function processA(state, action) {
  return {
    // Because you call them!
    somePart: processSomePartOfA(state.somePart),
    otherPart: doSomethingWithOtherPartOfA(state.otherPart)
  }
}
function doSomethingWithB(state, action) { ... }

function reducer(state = {}, action) {
  return {
    // Because you call them!
    a: processA(state.a, action),
    b: doSomethingWithB(state.b, action)
  };
}

// Not using the helper
let store = createStore(reducer);

You see? It's just functions calling functions. No magic “deep stuff”.
And you can use combineReducers many times too:

// Function names don't matter!
function processSomePartOfA(state, action) { ... }
function doSomethingWithOtherPartOfA(state, action) { ... }

/*
function processA(state, action) {
  return {
    somePart: processSomePartOfA(state.somePart),
    otherPart: doSomethingWithOtherPartOfA(state.otherPart)
  }
}
*/
let processA = combineReducers({
  somePart: processSomePartOfA,
  otherPart: doSomethingWithOtherPartOfA
});

function processA(state, action) { ... }
function doSomethingWithB(state, action) { ... }

/*
function reducer(state = {}, action) {
  return {
    a: processA(state.a, action),
    b: doSomethingWithB(state.b, action)
  };
}
*/
let reducer = combineReducers({
  a: processA,
  b: doSomethingWithB
});

let store = createStore(reducer);

The only part where function names matter is when you use export + import * as to “obtain” an object you pass to combineReducers because that's just how import * works! It puts things in object based on their export keys.

I think my biggest mistake here is assuming reader is familiar with named exports.

Contributor

gaearon commented Aug 9, 2015

Thanks for feedback, it's very valuable.

I want to stress that

Convention are part of API

is not really true.

We should probably just avoid import * in the docs because people assume it's integral part of the API when it's not at all, and it's just a convenient shortcut I use.

Function names only matter because of how ES6 export and import * as work. They have nothing to do with combineReducers API per se.

Btw, I'm still puzzled how to pass subpart of state to reducer. Should I camelCase join them or something? state={owner: {name: 'John'} } → export function ownerName(state = [], action)?

No :-). It's not a magical API. combineReducers(object) combines several reducers into one, and passes parts of state to its values by the keys you provide. It only does this exactly one level deep. There's no “build whole tree magic”. It's up to you to split a reducer into more functions:

// Function names don't matter!
function processA(state, action) { ... }
function doSomethingWithB(state, action) { ... }

function reducer(state = {}, action) {
  return {
    // Because you call them!
    a: processA(state.a, action),
    b: doSomethingWithB(state.b, action)
  };
}

// Not using combineReducers
let store = createStore(reducer);

They don't even matter if you use combineReducers helper as long as you create the object yourself:

// Function names don't matter!
function processA(state, action) { ... }
function doSomethingWithB(state, action) { ... }

/*
function reducer(state = {}, action) {
  return {
    a: processA(state.a, action),
    b: doSomethingWithB(state.b, action)
  };
}
*/
let reducer = combineReducers({
  a: processA,
  b: doSomethingWithB
});

let store = createStore(reducer);

You can do this many times.

Before:

// Function names don't matter!
function processSomePartOfA(state, action) { ... }
function doSomethingWithOtherPartOfA(state, action) { ... }

function processA(state, action) {
  return {
    // Because you call them!
    somePart: processSomePartOfA(state.somePart),
    otherPart: doSomethingWithOtherPartOfA(state.otherPart)
  }
}
function doSomethingWithB(state, action) { ... }

function reducer(state = {}, action) {
  return {
    // Because you call them!
    a: processA(state.a, action),
    b: doSomethingWithB(state.b, action)
  };
}

// Not using the helper
let store = createStore(reducer);

You see? It's just functions calling functions. No magic “deep stuff”.
And you can use combineReducers many times too:

// Function names don't matter!
function processSomePartOfA(state, action) { ... }
function doSomethingWithOtherPartOfA(state, action) { ... }

/*
function processA(state, action) {
  return {
    somePart: processSomePartOfA(state.somePart),
    otherPart: doSomethingWithOtherPartOfA(state.otherPart)
  }
}
*/
let processA = combineReducers({
  somePart: processSomePartOfA,
  otherPart: doSomethingWithOtherPartOfA
});

function processA(state, action) { ... }
function doSomethingWithB(state, action) { ... }

/*
function reducer(state = {}, action) {
  return {
    a: processA(state.a, action),
    b: doSomethingWithB(state.b, action)
  };
}
*/
let reducer = combineReducers({
  a: processA,
  b: doSomethingWithB
});

let store = createStore(reducer);

The only part where function names matter is when you use export + import * as to “obtain” an object you pass to combineReducers because that's just how import * works! It puts things in object based on their export keys.

I think my biggest mistake here is assuming reader is familiar with named exports.

@constb

This comment has been minimized.

Show comment
Hide comment
@constb

constb Aug 10, 2015

Contributor

I think my biggest mistake here is assuming reader is familiar with named exports.

I think, assuming reader is familiar with ES6 at all is a stretch. But that's what pushes people to learn and to figure this stuff out. So it's actually a good thing when reading is ahead of the reader's knowledge and experience.

Contributor

constb commented Aug 10, 2015

I think my biggest mistake here is assuming reader is familiar with named exports.

I think, assuming reader is familiar with ES6 at all is a stretch. But that's what pushes people to learn and to figure this stuff out. So it's actually a good thing when reading is ahead of the reader's knowledge and experience.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 12, 2015

Contributor

We're changing examples to explicitly call combineReducers in reducers/index so hopefully it's going to make more sense from now on: #473

Contributor

gaearon commented Aug 12, 2015

We're changing examples to explicitly call combineReducers in reducers/index so hopefully it's going to make more sense from now on: #473

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Sep 4, 2015

Contributor

Closing, as this seems to be better addressed in the current docs.
And we don't use import * in docs anymore either.

Contributor

gaearon commented Sep 4, 2015

Closing, as this seems to be better addressed in the current docs.
And we don't use import * in docs anymore either.

@gaearon gaearon closed this Sep 4, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment